links

lynx-like text mode web browser
git clone anongit@rnpnr.xyz:links.git
Log | Files | Refs | Feed | README | LICENSE

listedit.c (48640B)


      1 /* listedit.c
      2  * (c) 2002 Petr 'Brain' Kulhavy
      3  * This file is a part of the Links program, released under GPL.
      4  */
      5 
      6 #include "links.h"
      7 
      8 /*
      9                                     (#####)
     10                                   (#########)
     11                              ))))  (######)
     12                             ,C--O   (###)      _________________
     13                             |`:, \  ~~        |.  ,       v  , .|
     14                             `-__o  ~~  ;      |  ZAKAZ KOURENI  |
     15                             / _  \     `      |.               .|
     16                            | ( \__`_=k==      `~~~~~~~~~~~~~~~~~'
     17                            | `-/__---'
     18                            |=====C/
     19                            `.   ),'
     20                              \  /
     21                              ||_|
     22                              || |__
     23                              ||____)
     24 
     25                          v v  v   ,       ,v
     26                      KONECNE NEJAKE KOMENTARE...
     27 */
     28 
     29 /* Klikani myssi (nebo mysijou, jak rika Mikulas) v list-okne:
     30  * (hrozne dulezity komentar ;-P )
     31  *
     32  * Klikani je vyreseno nasledovne, pokud ma nekdo lepsi napad, nebo nejake
     33  * vyhrady, tak at mi je posle.
     34  *
     35  * Prostredni tlacitko+pohyb je scroll nahoru/dolu. Levym tlacitkem se nastavi
     36  * kurzor (cerna lista) na konkretni polozku. Kdyz se levym klikne na adresar
     37  * (na ty graficke nesmysly, ne na ten text), tak se adresar toggle
     38  * otevre/zavre. Prave tlacitko oznaci/odznaci polozku/adresar.
     39  */
     40 
     41 /* Premistovani polozek:
     42  *
     43  * Pravym tlacitkem se oznaci/odznaci polozka. Cudlikem "Odznacit vse" se
     44  * vsechny polozky odznaci. Cudlik "Prestehovat" presune vsechny oznacene
     45  * polozky za aktualni pozici, v tom poradi, jak jsou v seznamu. Pri zavreni
     46  * adresare se vsechny polozky v adresari odznaci.
     47  */
     48 
     49 /* Prekreslovani grafickych nesmyslu v okenku je samozrejme bez jedineho
     50  *                                                               v
     51  * bliknuti. Ne jako nejmenovane browsery... Proste obraz jako BIC (TM)
     52  */
     53 
     54 /* Ovladani klavesnici:
     55  * sipky, page up, page down, home end			pohyb
     56  * +							otevri adresar
     57  * -							zavri adresar
     58  * mezera						toggle adresar
     59  * ins, *, 8, i						toggle oznacit
     60  * ?, /, N, n						hledani nahoru, dolu,
     61  * znova, znova na druhou stranu
     62  */
     63 
     64 /*
     65  * Struktura struct list_decription obsahuje popis seznamu. Tenhle file
     66  * obsahuje obecne funkce k obsluze seznamu. Pomoci struct list_description se
     67  * seznam customizuje. Obecne funkce volaji funkce z list_description.
     68  *
     69  * Jedina funkce z tohoto filu, ktera se vola zvenku, je create_list_window. Ta
     70  * vyrobi a obstarava okno pro obsluhu seznamu.
     71  *
     72  * Obecny list neresi veci jako nahravani seznamu z filu, ukladani na disk
     73  * atd.(tyhle funkce si uzivatel musi napsat sam). Resi vlastne jenom to velke
     74  * okno na manipulaci se seznamem.
     75  */
     76 
     77 /*
     78  * Aby bylo konzistentni pridavani a editovani polozek, tak se musi pytlacit.
     79  *
     80  * To znamena, ze pri pridavani polozky do listu se vyrobi nova polozka
     81  * (NEPRIDA se do seznamu), pusti se edit a po zmacknuti OK se polozka prida do
     82  * seznamu. Pri zmacknuti cancel, se polozka smaze.
     83  *
     84  * Pri editovani polozky se vyrobi nova polozka, zkopiruje se do ni obsah te
     85  * puvodni (od toho tam je funkce copy_item), pak se zavola edit a podobne jako
     86  * v predchozim pripade: pri cancel se polozka smaze, pri OK se zkopiruje do
     87  * puvodni polozky a smaze se taky.
     88  *
     89  * O smazani polozky pri cancelu se bude starat uzivatelska funkce edit_item
     90  * sama. Funkce edit_item po zmacknuti OK zavola funkci, kterou dostane. Jako
     91  * 1. argument ji da data, ktera dostane, jako 2. argument ji preda pointer na
     92  * item.
     93  */
     94 
     95 /*
     96  * Seznam je definovan ve struct list. Muze byt bud placaty nebo stromovy.
     97  *
     98  * K placatemu asi neni co dodat. U placateho se ignoruje hloubka a neexistuji
     99  * adresare - vsechny polozky jsou si rovny (typ polozky se ignoruje).
    100  *
    101  * Stromovy seznam:
    102  * Kazdy clen seznamu ma flag sbaleno/rozbaleno. U itemy se to ignoruje, u
    103  * adresare to znamena, zda se zobrazuje obsah nebo ne. Aby rozbaleno/sbaleno
    104  * bylo u vsech prvku adresare, to by neslo: kdybych mel adresar a v nem dalsi
    105  * adresar, tak bych u toho vnoreneho adresare nevedel, jestli to
    106  * sbaleno/rozbaleno je od toho vnoreneho adresare, nebo od toho nad nim.
    107  *
    108  * Clenove seznamu maji hloubku - cislo od 0 vyse. Cim je prvek hloubeji ve
    109  * strukture, tim je hloubka vyssi. Obsah adresare s hloubkou x je souvisly blok
    110  * nasledujicich prvku s hloubkou >x.
    111  *
    112  * Hlava ma hloubku -1 a zobrazuje se taky jako clen seznamu (aby se dal
    113  * zobrazit prazdny seznam a dalo se do nej pridavat), takze se da vlastne cely
    114  * seznam zabalit/rozbalit. Hlava sice nema data, ale funkce type_item ji musi
    115  * umet zobrazit. Jako popis bude psat fixni text, napriklad "Bookmarks".
    116  *
    117  * Pro urychleni vykreslovani kazdy prvek v seznamu s adresarema obsahuje
    118  * pointer na otce (polozka fotr). U plocheho seznamu se tento pointer
    119  * ignoruje.
    120  *
    121  * Strukturu stromu bude vykreslovat obecna funkce (v tomto filu), protoze v
    122  * obecnem listu je struktura uz zachycena.
    123  */
    124 
    125 /*
    126  * V hlavnim okne se da nadefinovat 1 uzivatelske tlacitko. Polozka button ve
    127  * struct list_description obsahuje popisku tlacitka (kod stringu v
    128  * prekodovavacich tabulkach). Funkce button_fn je zavolana pri stisku
    129  * tlacitka, jako argument (void *) dostane aktualni polozku.  Pokud je
    130  * button_fn NULL, tlacitko se nekona.
    131  *
    132  * Toto tlacitko se da vyuzit napriklad u bookmarku, kde je potreba [ Goto ].
    133  *
    134  * Kdyz bude potreba predavat obsluzne funkci tlacitka nejake dalsi argumenty,
    135  * tak se pripadne definice obsluzne funkce prepise.
    136  *
    137  * Tlacitko funguje jen na polozky. Nefunguje na adresare (pokud se jedna o
    138  * stromovy list) ani na hlavu.
    139  */
    140 
    141 /* Jak funguje default_value:
    142  * kdyz se zmackne tlacitko add, zavola se funkce default_value, ktera si
    143  * naalokuje nejaky data pro new_item. Do funkce default_value se treba u
    144  * bookmarku umisti okopirovani altualniho nazvu a url stranky. Pak se zavola
    145  * new_item, ktera si prislusne hodnoty dekoduje a pomoci nich vyplni novou
    146  * polozku. Funkce new_item pak MUSI data dealokovat. Pokud funkce new_item
    147  * dostane misto pointeru s daty NULL, vyrobi prazdnou polozku.
    148  *
    149  * Default value musi vratit hodnoty v kodovani uvedenem v list_description
    150  */
    151 
    152 /* Pristupovani z vice linksu:
    153  *
    154  * ... se neresi - je zakazano. K tomu slouzi polozka open ve struct
    155  * list_description, ktera rika, jestli je okno uz otevrene, nebo ne.
    156  */
    157 
    158 /* Prekodovavani znakovych sad:
    159  *
    160  * type_item vraci text prekodovany do kodovani terminalu, ktery dostane.
    161  */
    162 
    163 /* struct list *current_pos;   current cursor position in the list */
    164 /* struct list *win_offset;    item at the top of the window */
    165 /* int win_pos;                current y position in the window */
    166 
    167 #define BOHNICE "+420-2-84016111"
    168 
    169 #define BFU_ELEMENT_EMPTY       0
    170 #define BFU_ELEMENT_PIPE        1
    171 #define BFU_ELEMENT_L           2
    172 #define BFU_ELEMENT_TEE         3
    173 #define BFU_ELEMENT_CLOSED      4
    174 #define BFU_ELEMENT_CLOSED_DOWN 5
    175 #define BFU_ELEMENT_OPEN        6
    176 #define BFU_ELEMENT_OPEN_DOWN   7
    177 
    178 /* for mouse scrolling */
    179 static long last_mouse_y;
    180 
    181 static void list_edit_toggle(struct dialog_data *dlg,
    182                              struct list_description *ld);
    183 
    184 #define sirka_scrollovadla 0
    185 
    186 /* This function uses these defines from setup.h:
    187  *
    188  * BFU_ELEMENT_WIDTH
    189  */
    190 
    191 /* draws one of BFU elements: | |- [-] [+] */
    192 /* BFU elements are used in the list window */
    193 /* this function also defines shape and size of the elements */
    194 /* returns width of the BFU element (all elements have the same size, but sizes
    195  * differ if we're in text mode or in graphics mode) */
    196 static int
    197 draw_bfu_element(struct terminal *term, int x, int y, unsigned char c, long b,
    198                  long f, unsigned char type, unsigned char selected)
    199 {
    200 	unsigned char vertical = 179;
    201 	unsigned char horizontal = 196;
    202 	unsigned char tee = 195;
    203 	unsigned char l = 192;
    204 
    205 	switch (type) {
    206 	case BFU_ELEMENT_EMPTY:
    207 		c |= ATTR_FRAME;
    208 		set_char(term, x, y, ' ', c);
    209 		set_char(term, x + 1, y, ' ', c);
    210 		set_char(term, x + 2, y, ' ', c);
    211 		set_char(term, x + 3, y, ' ', c);
    212 		set_char(term, x + 4, y, ' ', c);
    213 		break;
    214 
    215 	case BFU_ELEMENT_PIPE:
    216 		c |= ATTR_FRAME;
    217 		set_char(term, x, y, ' ', c);
    218 		set_char(term, x + 1, y, vertical, c);
    219 		set_char(term, x + 2, y, ' ', c);
    220 		set_char(term, x + 3, y, ' ', c);
    221 		set_char(term, x + 4, y, ' ', c);
    222 		break;
    223 
    224 	case BFU_ELEMENT_L:
    225 		c |= ATTR_FRAME;
    226 		set_char(term, x, y, ' ', c);
    227 		set_char(term, x + 1, y, l, c);
    228 		set_char(term, x + 2, y, horizontal, c);
    229 		set_char(term, x + 3, y, horizontal, c);
    230 		set_char(term, x + 4, y, ' ', c);
    231 		break;
    232 
    233 	case BFU_ELEMENT_TEE:
    234 		c |= ATTR_FRAME;
    235 		set_char(term, x, y, ' ', c);
    236 		set_char(term, x + 1, y, tee, c);
    237 		set_char(term, x + 2, y, horizontal, c);
    238 		set_char(term, x + 3, y, horizontal, c);
    239 		set_char(term, x + 4, y, ' ', c);
    240 		break;
    241 
    242 	case BFU_ELEMENT_CLOSED:
    243 	case BFU_ELEMENT_CLOSED_DOWN:
    244 		set_char(term, x, y, '[', c);
    245 		set_char(term, x + 1, y, '+', c);
    246 		set_char(term, x + 2, y, ']', c);
    247 		c |= ATTR_FRAME;
    248 		set_char(term, x + 3, y, horizontal, c);
    249 		set_char(term, x + 4, y, ' ', c);
    250 		break;
    251 
    252 	case BFU_ELEMENT_OPEN:
    253 	case BFU_ELEMENT_OPEN_DOWN:
    254 		set_char(term, x, y, '[', c);
    255 		set_char(term, x + 1, y, '-', c);
    256 		set_char(term, x + 2, y, ']', c);
    257 		c |= ATTR_FRAME;
    258 		set_char(term, x + 3, y, horizontal, c);
    259 		set_char(term, x + 4, y, ' ', c);
    260 		break;
    261 
    262 	default:
    263 		internal("draw_bfu_element: unknown BFU element type %d.\n",
    264 		         type);
    265 	}
    266 	if (selected)
    267 		set_char(term, x + 4, y, '*', c);
    268 	return BFU_ELEMENT_WIDTH; /* BFU element size in text mode */
    269 }
    270 
    271 /* aux structure for parameter exchange for redrawing list window */
    272 struct redraw_data {
    273 	struct list_description *ld;
    274 	struct dialog_data *dlg;
    275 	int n;
    276 };
    277 
    278 static void redraw_list(struct terminal *term, void *bla);
    279 
    280 /* returns next visible item in tree list */
    281 /* works only with visible items (head or any item returned by this function) */
    282 /* when list is flat returns next item */
    283 static struct list *
    284 next_in_tree(struct list_description *ld, struct list *item)
    285 {
    286 	int depth = item->depth;
    287 
    288 	/* flat list */
    289 	if (!ld->type)
    290 		return list_next(item);
    291 
    292 	if (!(item->type & 1) || (item->type & 2)) /* item or opened folder */
    293 		return list_next(item);
    294 	/* skip content of this folder */
    295 	do
    296 		item = list_next(item);
    297 	while (item->depth
    298 	       > depth); /* must stop on head 'cause it's depth is -1 */
    299 	return item;
    300 }
    301 
    302 /* returns previous visible item in tree list */
    303 /* works only with visible items (head or any item returned by this function) */
    304 /* when list is flat returns previous item */
    305 static struct list *
    306 prev_in_tree(struct list_description *ld, struct list *item)
    307 {
    308 	struct list *last_closed;
    309 	int depth = item->depth;
    310 
    311 	/* flat list */
    312 	if (!ld->type)
    313 		return list_prev(item);
    314 
    315 	if (item == ld->list)
    316 		depth = 0;
    317 
    318 	/* items with same or lower depth must be visible, because current item
    319 	 * is visible */
    320 	if (list_prev(item)->depth <= item->depth)
    321 		return list_prev(item);
    322 
    323 	/* find item followed with opened fotr's only */
    324 	/* searched item is last closed folder (going up from item) or
    325 	 * item->prev */
    326 	last_closed = list_prev(item);
    327 	item = list_prev(item);
    328 	while (1) {
    329 		if ((item->type & 3) == 1) /* closed folder */
    330 			last_closed = item;
    331 		if (item->depth <= depth)
    332 			break;
    333 		item = item->fotr;
    334 	}
    335 	return last_closed;
    336 }
    337 
    338 static int
    339 get_win_pos(struct list_description *ld)
    340 {
    341 	struct list *l;
    342 	int count = 0;
    343 
    344 	for (l = ld->win_offset; l != ld->current_pos; l = next_in_tree(ld, l))
    345 		count++;
    346 
    347 	return count;
    348 }
    349 
    350 static void
    351 unselect_in_folder(struct list_description *ld, struct list *l)
    352 {
    353 	int depth;
    354 
    355 	depth = l->depth;
    356 	for (l = list_next(l); l != ld->list && l->depth > depth;
    357 	     l = list_next(l))
    358 		l->type &= ~4;
    359 }
    360 
    361 /* aux function for list_item_add */
    362 static void
    363 list_insert_behind_item(struct dialog_data *dlg, struct list *pos,
    364                         struct list *item, struct list_description *ld)
    365 {
    366 	struct list *a;
    367 	struct redraw_data rd;
    368 
    369 	/*  BEFORE:  ... <----> pos <--(possible invisible items)--> a <---->
    370 	 * ... */
    371 	/*  AFTER:   ... <----> pos <--(possible invisible items)--> item <---->
    372 	 * a <----> ... */
    373 
    374 	a = next_in_tree(ld, pos);
    375 	add_before_pos(a, item);
    376 
    377 	/* if list is flat a->fotr will contain nosenses, but it won't crash ;-)
    378 	 */
    379 	if ((pos->type & 3) == 3 || pos->depth == -1) {
    380 		item->fotr = pos;
    381 		item->depth = pos->depth + 1;
    382 	} /* open directory or head */
    383 	else {
    384 		item->fotr = pos->fotr;
    385 		item->depth = pos->depth;
    386 	}
    387 
    388 	ld->current_pos =
    389 	    next_in_tree(ld, ld->current_pos); /* ld->current_pos->next==item */
    390 	ld->win_pos++;
    391 	if (ld->win_pos > ld->n_items - 1) { /* scroll down */
    392 		ld->win_pos = ld->n_items - 1;
    393 		ld->win_offset = next_in_tree(ld, ld->win_offset);
    394 	}
    395 
    396 	ld->modified = 1;
    397 
    398 	rd.ld = ld;
    399 	rd.dlg = dlg;
    400 	rd.n = 0;
    401 
    402 	draw_to_window(dlg->win, redraw_list, &rd);
    403 }
    404 
    405 /* aux function for list_item_edit */
    406 /* copies data of src to dest and calls free on the src */
    407 /* first argument is argument passed to user function */
    408 static void
    409 list_copy_item(struct dialog_data *dlg, struct list *dest, struct list *src,
    410                struct list_description *ld)
    411 {
    412 	struct redraw_data rd;
    413 
    414 	ld->copy_item(src, dest);
    415 	ld->delete_item(src);
    416 
    417 	ld->modified = 1; /* called after an successful edit */
    418 	rd.ld = ld;
    419 	rd.dlg = dlg;
    420 	rd.n = 0;
    421 
    422 	draw_to_window(dlg->win, redraw_list, &rd);
    423 }
    424 
    425 /* creates new item (calling new_item function) and calls edit_item function */
    426 static int
    427 list_item_add(struct dialog_data *dlg, struct dialog_item_data *useless)
    428 {
    429 	struct list_description *ld =
    430 	    (struct list_description *)dlg->dlg->udata2;
    431 	struct list *item = ld->current_pos;
    432 	struct list *new_item;
    433 
    434 	if (!(new_item = ld->new_item(ld->default_value ? ld->default_value(
    435 					  (struct session *)dlg->dlg->udata, 0)
    436 	                                                : NULL)))
    437 		return 1;
    438 	new_item->list_entry.prev = NULL;
    439 	new_item->list_entry.next = NULL;
    440 	new_item->type = 0;
    441 	new_item->depth = 0;
    442 
    443 	ld->edit_item(dlg, new_item, list_insert_behind_item, item, TITLE_ADD);
    444 	return 0;
    445 }
    446 
    447 /* like list_item_add but creates folder */
    448 static int
    449 list_folder_add(struct dialog_data *dlg, struct dialog_item_data *useless)
    450 {
    451 	struct list_description *ld =
    452 	    (struct list_description *)dlg->dlg->udata2;
    453 	struct list *item = ld->current_pos;
    454 	struct list *new_item;
    455 
    456 	if (!(new_item = ld->new_item(NULL)))
    457 		return 1;
    458 	new_item->list_entry.prev = NULL;
    459 	new_item->list_entry.next = NULL;
    460 	new_item->type = 1 | 2;
    461 	new_item->depth = 0;
    462 
    463 	ld->edit_item(dlg, new_item, list_insert_behind_item, item, TITLE_ADD);
    464 	return 0;
    465 }
    466 
    467 static int
    468 list_item_edit(struct dialog_data *dlg, struct dialog_item_data *useless)
    469 {
    470 	struct list_description *ld =
    471 	    (struct list_description *)dlg->dlg->udata2;
    472 	struct list *item = ld->current_pos;
    473 	struct list *new_item;
    474 
    475 	if (item == ld->list)
    476 		return 0; /* head */
    477 	if (!(new_item = ld->new_item(NULL)))
    478 		return 1;
    479 	new_item->list_entry.prev = NULL;
    480 	new_item->list_entry.next = NULL;
    481 
    482 	ld->copy_item(item, new_item);
    483 	ld->edit_item(dlg, new_item, list_copy_item, item, TITLE_EDIT);
    484 
    485 	return 0;
    486 }
    487 
    488 static inline int
    489 is_parent(struct list_description *ld, struct list *item, struct list *parent)
    490 {
    491 	struct list *l;
    492 
    493 	if (ld->type)
    494 		for (l = item; l->depth >= 0; l = l->fotr)
    495 			if (l == parent)
    496 				return 1;
    497 	return 0;
    498 }
    499 
    500 static int
    501 list_item_move(struct dialog_data *dlg, struct dialog_item_data *useless)
    502 {
    503 	struct list_description *ld =
    504 	    (struct list_description *)dlg->dlg->udata2;
    505 	struct list *i;
    506 	struct list *behind = ld->current_pos;
    507 	struct redraw_data rd;
    508 	int window_moved = 0;
    509 	int count = 0;
    510 
    511 	if (ld->current_pos->type
    512 	    & 4) { /* odznacime current_pos, kdyby nahodou byla oznacena */
    513 		count++;
    514 		ld->current_pos->type &= ~4;
    515 	}
    516 
    517 	for (i = list_next(ld->list); i != ld->list;) {
    518 		struct list *next = next_in_tree(ld, i);
    519 		struct list *prev = list_prev(i);
    520 		struct list *behind_next = next_in_tree(
    521 		    ld, behind); /* to se musi pocitat pokazdy, protoze by se
    522 		                    nam mohlo stat, ze to je taky oznaceny */
    523 		struct list *item_last =
    524 		    list_prev(next); /* last item of moved block */
    525 
    526 		if (!(i->type & 4)) {
    527 			i = next;
    528 			continue;
    529 		}
    530 		if (is_parent(
    531 			ld, ld->current_pos,
    532 			i)) { /* we're moving directory into itself - let's
    533 			         behave like item was not selected */
    534 			i->type &= ~4;
    535 			i = next;
    536 			count++;
    537 			continue;
    538 		}
    539 
    540 		if ((i->type & 3) == 3) { /* dirty trick */
    541 			i->type &= ~2;
    542 			next = next_in_tree(ld, i);
    543 			prev = list_prev(i);
    544 			item_last = list_prev(next);
    545 			i->type |= 2;
    546 		}
    547 
    548 		if (i == ld->win_offset) {
    549 			window_moved = 1;
    550 			if (next != ld->list)
    551 				ld->win_offset = next;
    552 		}
    553 
    554 		/* upravime fotrisko a hloubku */
    555 		if (ld->type) {
    556 			int a = i->depth;
    557 			struct list *l = i;
    558 
    559 			if ((behind->type & 3) == 3
    560 			    || behind == ld->list) { /* open folder or head */
    561 				i->fotr = behind;
    562 				i->depth = behind->depth + 1;
    563 			} else {
    564 				i->fotr = behind->fotr;
    565 				i->depth = behind->depth;
    566 			}
    567 			a = i->depth - a;
    568 
    569 			/* poopravime hloubku v adresari */
    570 			while (l != item_last) {
    571 				l = list_next(l);
    572 				l->depth += a;
    573 			}
    574 		}
    575 
    576 		if (behind_next == i)
    577 			goto predratovano; /* to uz je vsechno, akorat menime
    578 			                      hloubku */
    579 
    580 		/* predratujeme ukazatele kolem bloku na stare pozici */
    581 		prev->list_entry.next = &next->list_entry;
    582 		next->list_entry.prev = &prev->list_entry;
    583 
    584 		/* posuneme na novou pozici (tesne pred behind_next) */
    585 		i->list_entry.prev = behind_next->list_entry.prev;
    586 		behind_next->list_entry.prev->next = &i->list_entry;
    587 		item_last->list_entry.next = &behind_next->list_entry;
    588 		behind_next->list_entry.prev = &item_last->list_entry;
    589 
    590 predratovano:
    591 		/* odznacime */
    592 		i->type &= ~4;
    593 		unselect_in_folder(ld, i);
    594 
    595 		/* upravime pointery pro dalsi krok */
    596 		behind = i;
    597 		i = next;
    598 		count++;
    599 	}
    600 
    601 	if (window_moved) {
    602 		ld->current_pos = ld->win_offset;
    603 		ld->win_pos = 0;
    604 	} else
    605 		ld->win_pos = get_win_pos(ld);
    606 
    607 	if (!count) {
    608 		msg_box(dlg->win->term, /* terminal */
    609 		        NULL,           /* blocks to free */
    610 		        TEXT_(T_MOVE),  /* title */
    611 		        AL_CENTER,      /* alignment */
    612 		        TEXT_(T_NO_ITEMS_SELECTED), MSG_BOX_END, /* text */
    613 		        NULL,                                    /* data */
    614 		        1, /* # of buttons */
    615 		        TEXT_(T_CANCEL), msg_box_null,
    616 		        B_ESC | B_ENTER /* button1 */
    617 		);
    618 	} else {
    619 		ld->modified = 1;
    620 		rd.ld = ld;
    621 		rd.dlg = dlg;
    622 		rd.n = 0;
    623 		draw_to_window(dlg->win, redraw_list, &rd);
    624 	}
    625 	return 0;
    626 }
    627 
    628 /* unselect all items */
    629 static int
    630 list_item_unselect(struct dialog_data *dlg, struct dialog_item_data *useless)
    631 {
    632 	struct list_description *ld =
    633 	    (struct list_description *)dlg->dlg->udata2;
    634 	struct list *item;
    635 	struct redraw_data rd;
    636 
    637 	item = ld->list;
    638 	do {
    639 		item->type &= ~4;
    640 		item = list_next(item);
    641 	} while (item != ld->list);
    642 
    643 	rd.ld = ld;
    644 	rd.dlg = dlg;
    645 	rd.n = 0;
    646 
    647 	draw_to_window(dlg->win, redraw_list, &rd);
    648 	return 0;
    649 }
    650 
    651 /* user button function - calls button_fn with current item */
    652 static int
    653 list_item_button(struct dialog_data *dlg, struct dialog_item_data *useless)
    654 {
    655 	struct list_description *ld =
    656 	    (struct list_description *)dlg->dlg->udata2;
    657 	struct list *item = ld->current_pos;
    658 	struct session *ses = (struct session *)dlg->dlg->udata;
    659 
    660 	if (!ld->button_fn)
    661 		internal("Links got schizophrenia! Call " BOHNICE ".\n");
    662 
    663 	if (item == ld->list || list_empty(item->list_entry))
    664 		return 0; /* head or empty list */
    665 
    666 	if (ld->type && (item->type & 1)) {
    667 		list_edit_toggle(dlg, ld);
    668 		return 0; /* this is tree list and item is directory */
    669 	}
    670 
    671 	ld->button_fn(ses, item);
    672 	cancel_dialog(dlg, useless);
    673 	return 0;
    674 }
    675 
    676 struct ve_skodarne_je_jeste_vetsi_narez {
    677 	struct list_description *ld;
    678 	struct dialog_data *dlg;
    679 	struct list *item;
    680 };
    681 
    682 /* when delete is confirmed adjusts current_pos and calls delete function */
    683 static void
    684 delete_ok(void *data)
    685 {
    686 	struct ve_skodarne_je_jeste_vetsi_narez *skd =
    687 	    (struct ve_skodarne_je_jeste_vetsi_narez *)data;
    688 	struct list_description *ld = skd->ld;
    689 	struct list *item = skd->item;
    690 	struct dialog_data *dlg = skd->dlg;
    691 	struct redraw_data rd;
    692 
    693 	/* strong premise: we can't delete head of the list */
    694 	if (list_next(ld->current_pos) != ld->list) {
    695 		if (ld->current_pos == ld->win_offset)
    696 			ld->win_offset = list_next(ld->current_pos);
    697 		ld->current_pos = list_next(ld->current_pos);
    698 	} else {                  /* last item */
    699 		if (!ld->win_pos) /* only one line on the screen */
    700 			ld->win_offset = prev_in_tree(ld, ld->win_offset);
    701 		else
    702 			ld->win_pos--;
    703 		ld->current_pos = prev_in_tree(ld, ld->current_pos);
    704 	}
    705 
    706 	ld->delete_item(item);
    707 
    708 	rd.ld = ld;
    709 	rd.dlg = dlg;
    710 	rd.n = 0;
    711 
    712 	ld->modified = 1;
    713 	draw_to_window(dlg->win, redraw_list, &rd);
    714 }
    715 
    716 /* when delete folder is confirmed adjusts current_pos and calls delete function
    717  */
    718 static void
    719 delete_folder_recursively(void *data)
    720 {
    721 	struct ve_skodarne_je_jeste_vetsi_narez *skd =
    722 	    (struct ve_skodarne_je_jeste_vetsi_narez *)data;
    723 	struct list_description *ld = skd->ld;
    724 	struct list *item = skd->item;
    725 	struct dialog_data *dlg = skd->dlg;
    726 	struct redraw_data rd;
    727 	struct list *i, *j;
    728 	int depth;
    729 
    730 	for (i = list_next(item), depth = item->depth;
    731 	     i != ld->list && i->depth > depth;) {
    732 		j = i;
    733 		i = list_next(i);
    734 		ld->delete_item(j);
    735 	}
    736 
    737 	/* strong premise: we can't delete head of the list */
    738 	if (list_next(ld->current_pos) != ld->list) {
    739 		if (ld->current_pos == ld->win_offset)
    740 			ld->win_offset = list_next(ld->current_pos);
    741 		ld->current_pos = list_next(ld->current_pos);
    742 	} else {                  /* last item */
    743 		if (!ld->win_pos) /* only one line on the screen */
    744 			ld->win_offset = prev_in_tree(ld, ld->win_offset);
    745 		else
    746 			ld->win_pos--;
    747 		ld->current_pos = prev_in_tree(ld, ld->current_pos);
    748 	}
    749 
    750 	ld->delete_item(item);
    751 
    752 	rd.ld = ld;
    753 	rd.dlg = dlg;
    754 	rd.n = 0;
    755 
    756 	ld->modified = 1;
    757 	draw_to_window(dlg->win, redraw_list, &rd);
    758 }
    759 
    760 /* tests if directory is emty */
    761 static int
    762 is_empty_dir(struct list_description *ld, struct list *dir)
    763 {
    764 	if (!ld->type)
    765 		return 1; /* flat list */
    766 	if (!(dir->type & 1))
    767 		return 1; /* not a directory */
    768 
    769 	return list_next(dir)->depth <= dir->depth; /* head depth is -1 */
    770 }
    771 
    772 /* delete dialog */
    773 static int
    774 list_item_delete(struct dialog_data *dlg, struct dialog_item_data *useless)
    775 {
    776 	struct terminal *term = dlg->win->term;
    777 	struct list_description *ld =
    778 	    (struct list_description *)(dlg->dlg->udata2);
    779 	struct list *item = ld->current_pos;
    780 	unsigned char *txt;
    781 	struct ve_skodarne_je_jeste_vetsi_narez *narez;
    782 
    783 	if (item == ld->list || list_empty(item->list_entry))
    784 		return 0; /* head or empty list */
    785 
    786 	narez = xmalloc(sizeof(struct ve_skodarne_je_jeste_vetsi_narez));
    787 	narez->ld = ld;
    788 	narez->item = item;
    789 	narez->dlg = dlg;
    790 
    791 	txt = ld->type_item(term, item, 0);
    792 	if (!txt) {
    793 		txt = stracpy(cast_uchar "");
    794 	}
    795 
    796 	if ((item->type) & 1) /* folder */
    797 	{
    798 		if (!is_empty_dir(ld, item))
    799 			msg_box(term,                    /* terminal */
    800 			        getml(txt, narez, NULL), /* blocks to free */
    801 			        TEXT_(T_DELETE_FOLDER),  /* title */
    802 			        AL_CENTER,               /* alignment */
    803 			        TEXT_(T_FOLDER), cast_uchar " \"", txt,
    804 			        cast_uchar "\" ",
    805 			        TEXT_(T_NOT_EMPTY_SURE_DELETE),
    806 			        MSG_BOX_END,   /* text */
    807 			        (void *)narez, /* data for ld->delete_item */
    808 			        2,             /* # of buttons */
    809 			        TEXT_(T_NO), msg_box_null, B_ESC, /* button1 */
    810 			        TEXT_(T_YES), delete_folder_recursively,
    811 			        B_ENTER /* button2 */
    812 			);
    813 		else
    814 			msg_box(term,                    /* terminal */
    815 			        getml(txt, narez, NULL), /* blocks to free */
    816 			        TEXT_(T_DELETE_FOLDER),  /* title */
    817 			        AL_CENTER,               /* alignment */
    818 			        TEXT_(T_SURE_DELETE), cast_uchar " ",
    819 			        TEXT_(T_fOLDER), cast_uchar " \"", txt,
    820 			        cast_uchar "\"?",
    821 			        MSG_BOX_END,   /* null-terminated text */
    822 			        (void *)narez, /* data for ld->delete_item */
    823 			        2,             /* # of buttons */
    824 			        TEXT_(T_YES), delete_ok, B_ENTER, /* button1 */
    825 			        TEXT_(T_NO), msg_box_null, B_ESC  /* button2 */
    826 			);
    827 	} else                                          /* item */
    828 		msg_box(term,                           /* terminal */
    829 		        getml(txt, narez, NULL),        /* blocks to free */
    830 		        TEXT_(ld->delete_dialog_title), /* title */
    831 		        AL_CENTER,                      /* alignment */
    832 		        TEXT_(T_SURE_DELETE), cast_uchar " ",
    833 		        TEXT_(ld->item_description), cast_uchar " \"", txt,
    834 		        cast_uchar "\"?",
    835 		        MSG_BOX_END,   /* null-terminated text */
    836 		        (void *)narez, /* data for ld->delete_item */
    837 		        2,             /* # of buttons */
    838 		        TEXT_(T_YES), delete_ok, B_ENTER, /* button1 */
    839 		        TEXT_(T_NO), msg_box_null, B_ESC  /* button2 */
    840 		);
    841 	return 0;
    842 }
    843 
    844 static void
    845 redraw_list_element(struct terminal *term, struct dialog_data *dlg, int y,
    846                     int w, struct list_description *ld, struct list *l)
    847 {
    848 	struct list *lx;
    849 	unsigned char *xp;
    850 	int xd;
    851 	unsigned char *txt;
    852 	int x = 0;
    853 	unsigned char color = 0;
    854 	long bgcolor = 0, fgcolor = 0;
    855 	int b;
    856 	unsigned char element;
    857 
    858 	color = l == ld->current_pos ? COLOR_MENU_SELECTED : COLOR_MENU_TEXT;
    859 
    860 	txt = ld->type_item(term, l, 1);
    861 	if (!txt) {
    862 		txt = stracpy(cast_uchar "");
    863 	}
    864 
    865 	/* everything except head */
    866 
    867 	if (l != ld->list) {
    868 		switch (ld->type) {
    869 		case 0:
    870 			element = BFU_ELEMENT_TEE;
    871 			if (list_next(l) == ld->list)
    872 				element = BFU_ELEMENT_L;
    873 			x += draw_bfu_element(term, dlg->x + DIALOG_LB, y,
    874 			                      color, bgcolor, fgcolor, element,
    875 			                      l->type & 4);
    876 			break;
    877 		case 1:
    878 			xp = xmalloc(l->depth + 1);
    879 			memset(xp, 0, l->depth + 1);
    880 			xd = l->depth + 1;
    881 			for (lx = list_next(l); lx != ld->list;
    882 			     lx = list_next(lx)) {
    883 				if (lx->depth < xd) {
    884 					xd = lx->depth;
    885 					xp[xd] = 1;
    886 					if (!xd)
    887 						break;
    888 				}
    889 			}
    890 			for (b = 0; b < l->depth; b++)
    891 				x += draw_bfu_element(term,
    892 				                      dlg->x + DIALOG_LB + x, y,
    893 				                      color, bgcolor, fgcolor,
    894 				                      xp[b] ? BFU_ELEMENT_PIPE
    895 				                            : BFU_ELEMENT_EMPTY,
    896 				                      0);
    897 			if (l->depth >= 0) { /* everything except head */
    898 				int o = xp[l->depth];
    899 				switch (l->type & 1) {
    900 				case 0: /* item */
    901 					element =
    902 					    o ? BFU_ELEMENT_TEE : BFU_ELEMENT_L;
    903 					break;
    904 
    905 				case 1: /* directory */
    906 					if (l->type & 2) {
    907 						element =
    908 						    o ? BFU_ELEMENT_OPEN_DOWN
    909 						      : BFU_ELEMENT_OPEN;
    910 					} else {
    911 						element =
    912 						    o ? BFU_ELEMENT_CLOSED_DOWN
    913 						      : BFU_ELEMENT_CLOSED;
    914 					}
    915 					break;
    916 
    917 				default: /* this should never happen */
    918 					internal("=8-Q  lunacy level too high! "
    919 					         "Call " BOHNICE ".\n");
    920 					element = BFU_ELEMENT_EMPTY;
    921 				}
    922 				x += draw_bfu_element(
    923 				    term, dlg->x + DIALOG_LB + x, y, color,
    924 				    bgcolor, fgcolor, element, l->type & 4);
    925 			}
    926 			free(xp);
    927 			break;
    928 		default:
    929 			internal("Invalid list description type.\n"
    930 			         "Somebody's probably shooting into memory.\n"
    931 			         "_______________\n"
    932 			         "`--|_____|--|___ `\\\n"
    933 			         "             \"  \\___\\\n");
    934 		}
    935 	}
    936 
    937 	print_text(term, dlg->x + x + DIALOG_LB, y, w - x, txt, color);
    938 	x += strlen((char *)txt);
    939 	fill_area(term, dlg->x + DIALOG_LB + x, y, w - x, 1, ' ', 0);
    940 	set_line_color(term, dlg->x + DIALOG_LB + x, y, w - x, color);
    941 	free(txt);
    942 }
    943 
    944 /* redraws list */
    945 static void
    946 redraw_list(struct terminal *term, void *bla)
    947 {
    948 	struct redraw_data *rd = (struct redraw_data *)bla;
    949 	struct list_description *ld = rd->ld;
    950 	struct dialog_data *dlg = rd->dlg;
    951 	int y, a;
    952 	struct list *l;
    953 	int w = dlg->xw - 2 * DIALOG_LB;
    954 	y = dlg->y + DIALOG_TB;
    955 
    956 	for (a = 0, l = ld->win_offset; a < ld->n_items;) {
    957 		redraw_list_element(term, dlg, y, w, ld, l);
    958 		l = next_in_tree(ld, l);
    959 		a++;
    960 		y++;
    961 		if (l == ld->list)
    962 			break;
    963 	}
    964 	fill_area(term, dlg->x + DIALOG_LB, y, w, ld->n_items - a, ' ',
    965 	          COLOR_MENU_TEXT);
    966 }
    967 
    968 /* moves cursor from old position to a new one */
    969 /* direction: -1=old is previous, +1=old is next */
    970 static void
    971 redraw_list_line(struct terminal *term, void *bla)
    972 {
    973 	struct redraw_data *rd = (struct redraw_data *)bla;
    974 	struct list_description *ld = rd->ld;
    975 	struct dialog_data *dlg = rd->dlg;
    976 	int direction = rd->n;
    977 	int w = dlg->xw - 2 * DIALOG_LB;
    978 	int y = dlg->y + DIALOG_TB + ld->win_pos;
    979 	struct list *l;
    980 
    981 	redraw_list_element(term, dlg, y, w, ld, ld->current_pos);
    982 	if (!term->spec->block_cursor) {
    983 		set_cursor(term, dlg->x + DIALOG_LB, y, dlg->x + DIALOG_LB, y);
    984 	}
    985 	y += direction;
    986 	switch (direction) {
    987 	case 0:
    988 		l = NULL;
    989 		break;
    990 	case 1:
    991 		l = next_in_tree(ld, ld->current_pos);
    992 		break;
    993 	case -1:
    994 		l = prev_in_tree(ld, ld->current_pos);
    995 		break;
    996 	default:
    997 		internal("redraw_list_line: invalid direction %d", direction);
    998 		l = NULL;
    999 		break;
   1000 	}
   1001 	if (l)
   1002 		redraw_list_element(term, dlg, y, w, ld, l);
   1003 }
   1004 
   1005 /* like redraw_list, but scrolls window, prints new line to top/bottom */
   1006 /* in text mode calls redraw list */
   1007 /* direction: -1=up, 1=down */
   1008 static void
   1009 scroll_list(struct terminal *term, void *bla)
   1010 {
   1011 	redraw_list(term, bla);
   1012 }
   1013 
   1014 static void
   1015 list_find_next(struct redraw_data *rd, int direction)
   1016 {
   1017 	struct list_description *ld = rd->ld;
   1018 	struct dialog_data *dlg = rd->dlg;
   1019 	struct session *ses = (struct session *)(dlg->dlg->udata);
   1020 	struct list *item;
   1021 
   1022 	if (!ld->search_word) {
   1023 		msg_box(ses->term, NULL, TEXT_(T_SEARCH), AL_CENTER,
   1024 		        TEXT_(T_NO_PREVIOUS_SEARCH), MSG_BOX_END, NULL, 1,
   1025 		        TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
   1026 		return;
   1027 	}
   1028 
   1029 	if ((item =
   1030 	         ld->find_item(ld->current_pos, ld->search_word, direction))) {
   1031 		struct list *l;
   1032 		ld->current_pos = item;
   1033 		ld->win_offset = item;
   1034 		ld->win_pos = 0;
   1035 		if (ld->type)
   1036 			for (l = item; l->depth >= 0; l = l->fotr)
   1037 				if (l != item)
   1038 					l->type |= 2;
   1039 
   1040 		draw_to_window(dlg->win, redraw_list, rd);
   1041 		if (!ses->term->spec->block_cursor)
   1042 			set_cursor(ses->term, dlg->x + DIALOG_LB,
   1043 			           dlg->y + DIALOG_TB + ld->win_pos,
   1044 			           dlg->x + DIALOG_LB,
   1045 			           dlg->y + DIALOG_TB + ld->win_pos);
   1046 	} else
   1047 		msg_box(ses->term, NULL, TEXT_(T_SEARCH), AL_CENTER,
   1048 		        TEXT_(T_SEARCH_STRING_NOT_FOUND), MSG_BOX_END, NULL, 1,
   1049 		        TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
   1050 }
   1051 
   1052 static void
   1053 list_search_for_back(void *rd_, unsigned char *str)
   1054 {
   1055 	struct redraw_data *rd = (struct redraw_data *)rd_;
   1056 	struct list_description *ld = rd->ld;
   1057 
   1058 	if (!*str)
   1059 		return;
   1060 	if (!ld->open)
   1061 		return;
   1062 
   1063 	free(ld->search_word);
   1064 	ld->search_word = to_utf8_upcase(str, 0);
   1065 	ld->search_direction = -1;
   1066 
   1067 	list_find_next(rd, ld->search_direction);
   1068 }
   1069 
   1070 static void
   1071 list_search_for(void *rd_, unsigned char *str)
   1072 {
   1073 	struct redraw_data *rd = (struct redraw_data *)rd_;
   1074 	struct list_description *ld = rd->ld;
   1075 
   1076 	if (!*str)
   1077 		return;
   1078 	if (!ld->open)
   1079 		return;
   1080 
   1081 	free(ld->search_word);
   1082 	ld->search_word = to_utf8_upcase(str, 0);
   1083 	ld->search_direction = 1;
   1084 
   1085 	list_find_next(rd, ld->search_direction);
   1086 }
   1087 
   1088 static void
   1089 list_edit_toggle(struct dialog_data *dlg, struct list_description *ld)
   1090 {
   1091 	static struct redraw_data rd;
   1092 	ld->current_pos->type ^= 2;
   1093 	if (!(ld->current_pos->type & 2))
   1094 		unselect_in_folder(ld, ld->current_pos);
   1095 	rd.ld = ld;
   1096 	rd.dlg = dlg;
   1097 	rd.n = 0;
   1098 	draw_to_window(dlg->win, redraw_list, &rd);
   1099 	draw_to_window(dlg->win, redraw_list_line, &rd); /* set cursor */
   1100 }
   1101 
   1102 static int
   1103 list_event_handler(struct dialog_data *dlg, struct links_event *ev)
   1104 {
   1105 	struct list_description *ld =
   1106 	    (struct list_description *)(dlg->dlg->udata2);
   1107 	static struct redraw_data rd;
   1108 	struct session *ses = (struct session *)(dlg->dlg->udata);
   1109 
   1110 	rd.ld = ld;
   1111 	rd.dlg = dlg;
   1112 	rd.n = 0;
   1113 
   1114 	switch ((int)ev->ev) {
   1115 	case EV_KBD:
   1116 		if (ev->y & KBD_PASTING)
   1117 			break;
   1118 		if (ld->type == 1) /* tree list */
   1119 		{
   1120 			if (ev->x == ' ') /* toggle folder */
   1121 			{
   1122 				if (!((ld->current_pos->type) & 1))
   1123 					return EVENT_PROCESSED; /* item */
   1124 
   1125 				list_edit_toggle(dlg, ld);
   1126 				return EVENT_PROCESSED;
   1127 			}
   1128 			if (ev->x == '+' || ev->x == '=') /* open folder */
   1129 			{
   1130 				if (!((ld->current_pos->type) & 1))
   1131 					return EVENT_PROCESSED; /* item */
   1132 				if ((ld->current_pos->type) & 2)
   1133 					return EVENT_PROCESSED; /* already open
   1134 					                         */
   1135 				list_edit_toggle(dlg, ld);
   1136 				return EVENT_PROCESSED;
   1137 			}
   1138 			if (ev->x == '-') /* close folder */
   1139 			{
   1140 				if (!((ld->current_pos->type) & 1))
   1141 					return EVENT_PROCESSED; /* item */
   1142 				if (!((ld->current_pos->type) & 2))
   1143 					return EVENT_PROCESSED; /* already
   1144 					                           closed */
   1145 				list_edit_toggle(dlg, ld);
   1146 				return EVENT_PROCESSED;
   1147 			}
   1148 		}
   1149 		if (ev->x == '/'
   1150 		    || (ev->x == KBD_FIND
   1151 		        && !(ev->y
   1152 		             & (KBD_SHIFT | KBD_CTRL
   1153 		                | KBD_ALT)))) /* search forward */
   1154 		{
   1155 			struct redraw_data *r;
   1156 
   1157 			r = xmalloc(sizeof(struct redraw_data));
   1158 			r->ld = ld;
   1159 			r->dlg = dlg;
   1160 
   1161 			input_field(ses->term, getml(r, NULL), TEXT_(T_SEARCH),
   1162 			            TEXT_(T_SEARCH_FOR_TEXT), r,
   1163 			            ld->search_history, MAX_INPUT_URL_LEN,
   1164 			            cast_uchar "", 0, 0, NULL, 2, TEXT_(T_OK),
   1165 			            list_search_for, TEXT_(T_CANCEL),
   1166 			            input_field_null);
   1167 			return EVENT_PROCESSED;
   1168 		}
   1169 		if (ev->x == '?'
   1170 		    || (ev->x == KBD_FIND
   1171 		        && ev->y
   1172 		               & (KBD_SHIFT | KBD_CTRL
   1173 		                  | KBD_ALT))) /* search back */
   1174 		{
   1175 			struct redraw_data *r;
   1176 
   1177 			r = xmalloc(sizeof(struct redraw_data));
   1178 			r->ld = ld;
   1179 			r->dlg = dlg;
   1180 
   1181 			input_field(
   1182 			    ses->term, getml(r, NULL), TEXT_(T_SEARCH_BACK),
   1183 			    TEXT_(T_SEARCH_FOR_TEXT), r, ld->search_history,
   1184 			    MAX_INPUT_URL_LEN, cast_uchar "", 0, 0, NULL, 2,
   1185 			    TEXT_(T_OK), list_search_for_back, TEXT_(T_CANCEL),
   1186 			    input_field_null);
   1187 			return EVENT_PROCESSED;
   1188 		}
   1189 		if ((ev->x == 'n' && !(ev->y & KBD_ALT))
   1190 		    || ev->x == KBD_REDO) /* find next */
   1191 		{
   1192 			list_find_next(&rd, ld->search_direction);
   1193 			return EVENT_PROCESSED;
   1194 		}
   1195 		if ((ev->x == 'N' && !(ev->y & KBD_ALT))
   1196 		    || ev->x == KBD_UNDO) /* find prev */
   1197 		{
   1198 			list_find_next(&rd, -ld->search_direction);
   1199 			return EVENT_PROCESSED;
   1200 		}
   1201 		if (ev->x == KBD_UP) {
   1202 			if (ld->current_pos == ld->list)
   1203 				goto kbd_up_redraw_exit; /* already on the top
   1204 				                          */
   1205 			ld->current_pos = prev_in_tree(ld, ld->current_pos);
   1206 			ld->win_pos--;
   1207 			rd.n = 1;
   1208 			if (ld->win_pos < 0) /* scroll up */
   1209 			{
   1210 				ld->win_pos = 0;
   1211 				ld->win_offset =
   1212 				    prev_in_tree(ld, ld->win_offset);
   1213 				draw_to_window(dlg->win, scroll_list, &rd);
   1214 			}
   1215 kbd_up_redraw_exit:
   1216 			draw_to_window(dlg->win, redraw_list_line, &rd);
   1217 			return EVENT_PROCESSED;
   1218 		}
   1219 		if (ev->x == 'i' || ev->x == '*' || ev->x == '8'
   1220 		    || ev->x == KBD_INS || ev->x == KBD_SELECT) {
   1221 			if (ld->current_pos != ld->list)
   1222 				ld->current_pos->type ^= 4;
   1223 			rd.n = -1;
   1224 			if (next_in_tree(ld, ld->current_pos)
   1225 			    == ld->list) /* already at the bottom */
   1226 			{
   1227 				draw_to_window(dlg->win, redraw_list_line, &rd);
   1228 				return EVENT_PROCESSED;
   1229 			}
   1230 			ld->current_pos = next_in_tree(ld, ld->current_pos);
   1231 			ld->win_pos++;
   1232 			if (ld->win_pos > ld->n_items - 1) /* scroll down */
   1233 			{
   1234 				ld->win_pos = ld->n_items - 1;
   1235 				ld->win_offset =
   1236 				    next_in_tree(ld, ld->win_offset);
   1237 				draw_to_window(dlg->win, scroll_list, &rd);
   1238 			}
   1239 			draw_to_window(dlg->win, redraw_list_line, &rd);
   1240 			return EVENT_PROCESSED;
   1241 		}
   1242 		if (ev->x == KBD_DOWN) {
   1243 			if (next_in_tree(ld, ld->current_pos) == ld->list)
   1244 				goto kbd_down_redraw_exit; /* already at the
   1245 				                              bottom */
   1246 			ld->current_pos = next_in_tree(ld, ld->current_pos);
   1247 			ld->win_pos++;
   1248 			rd.n = -1;
   1249 			if (ld->win_pos > ld->n_items - 1) /* scroll down */
   1250 			{
   1251 				ld->win_pos = ld->n_items - 1;
   1252 				ld->win_offset =
   1253 				    next_in_tree(ld, ld->win_offset);
   1254 				draw_to_window(dlg->win, scroll_list, &rd);
   1255 			}
   1256 kbd_down_redraw_exit:
   1257 			draw_to_window(dlg->win, redraw_list_line, &rd);
   1258 			return EVENT_PROCESSED;
   1259 		}
   1260 		if (ev->x == KBD_HOME
   1261 		    || (upcase(ev->x) == 'A' && ev->y & KBD_CTRL)) {
   1262 			if (ld->current_pos == ld->list)
   1263 				goto kbd_home_redraw_exit; /* already on the top
   1264 				                            */
   1265 			ld->win_offset = ld->list;
   1266 			ld->current_pos = ld->win_offset;
   1267 			ld->win_pos = 0;
   1268 			rd.n = 0;
   1269 			draw_to_window(dlg->win, redraw_list, &rd);
   1270 kbd_home_redraw_exit:
   1271 			draw_to_window(dlg->win, redraw_list_line,
   1272 			               &rd); /* set cursor */
   1273 			return EVENT_PROCESSED;
   1274 		}
   1275 		if (ev->x == KBD_END
   1276 		    || (upcase(ev->x) == 'E' && ev->y & KBD_CTRL)) {
   1277 			int a;
   1278 
   1279 			if (ld->current_pos == prev_in_tree(ld, ld->list))
   1280 				goto kbd_end_redraw_exit; /* already on the top
   1281 				                           */
   1282 			ld->win_offset = prev_in_tree(ld, ld->list);
   1283 			for (a = 1;
   1284 			     a < ld->n_items && ld->win_offset != ld->list; a++)
   1285 				ld->win_offset =
   1286 				    prev_in_tree(ld, ld->win_offset);
   1287 			ld->current_pos = prev_in_tree(ld, ld->list);
   1288 			ld->win_pos = a - 1;
   1289 			rd.n = 0;
   1290 			draw_to_window(dlg->win, redraw_list, &rd);
   1291 kbd_end_redraw_exit:
   1292 			draw_to_window(dlg->win, redraw_list_line,
   1293 			               &rd); /* set cursor */
   1294 			return EVENT_PROCESSED;
   1295 		}
   1296 		if (ev->x == KBD_PAGE_UP
   1297 		    || (upcase(ev->x) == 'B' && ev->y & KBD_CTRL)) {
   1298 			int a;
   1299 
   1300 			if (ld->current_pos == ld->list)
   1301 				goto kbd_page_up_redraw_exit; /* already on the
   1302 				                                 top */
   1303 			for (a = 0;
   1304 			     a < ld->n_items && ld->win_offset != ld->list;
   1305 			     a++) {
   1306 				ld->win_offset =
   1307 				    prev_in_tree(ld, ld->win_offset);
   1308 				ld->current_pos =
   1309 				    prev_in_tree(ld, ld->current_pos);
   1310 			}
   1311 			if (a < ld->n_items) {
   1312 				ld->current_pos = ld->win_offset;
   1313 				ld->win_pos = 0;
   1314 			}
   1315 			rd.n = 0;
   1316 			draw_to_window(dlg->win, redraw_list, &rd);
   1317 kbd_page_up_redraw_exit:
   1318 			draw_to_window(dlg->win, redraw_list_line,
   1319 			               &rd); /* set cursor */
   1320 			return EVENT_PROCESSED;
   1321 		}
   1322 		if (ev->x == KBD_PAGE_DOWN
   1323 		    || (upcase(ev->x) == 'F' && ev->y & KBD_CTRL)) {
   1324 			int a;
   1325 			struct list *p = ld->win_offset;
   1326 
   1327 			if (ld->current_pos == prev_in_tree(ld, ld->list))
   1328 				goto kbd_page_down_redraw_exit; /* already on
   1329 				                                   the bottom */
   1330 			for (a = 0;
   1331 			     a < ld->n_items && ld->list != next_in_tree(ld, p);
   1332 			     a++)
   1333 				p = next_in_tree(ld, p);
   1334 			if (a < ld->n_items) /* already last screen */
   1335 			{
   1336 				ld->current_pos = p;
   1337 				ld->win_pos = a;
   1338 				rd.n = 0;
   1339 				draw_to_window(dlg->win, redraw_list, &rd);
   1340 				draw_to_window(dlg->win, redraw_list_line,
   1341 				               &rd); /* set cursor */
   1342 				return EVENT_PROCESSED;
   1343 			}
   1344 			/* here is whole screen only - the window was full
   1345 			 * before pressing the page-down key */
   1346 			/* p is pointing behind last item of the window (behind
   1347 			 * last visible item in the window) */
   1348 			for (a = 0; a < ld->n_items && p != ld->list; a++) {
   1349 				ld->win_offset =
   1350 				    next_in_tree(ld, ld->win_offset);
   1351 				ld->current_pos =
   1352 				    next_in_tree(ld, ld->current_pos);
   1353 				p = next_in_tree(ld, p);
   1354 			}
   1355 			if (a < ld->n_items) {
   1356 				ld->current_pos = prev_in_tree(ld, ld->list);
   1357 				ld->win_pos = ld->n_items - 1;
   1358 			}
   1359 			rd.n = 0;
   1360 			draw_to_window(dlg->win, redraw_list, &rd);
   1361 kbd_page_down_redraw_exit:
   1362 			draw_to_window(dlg->win, redraw_list_line,
   1363 			               &rd); /* set cursor */
   1364 			return EVENT_PROCESSED;
   1365 		}
   1366 		break;
   1367 
   1368 	case EV_MOUSE:
   1369 		/* toggle select item */
   1370 		if ((ev->b & BM_ACT) == B_DOWN
   1371 		    && (ev->b & BM_BUTT) == B_RIGHT) {
   1372 			int n, a;
   1373 			struct list *l = ld->win_offset;
   1374 
   1375 			last_mouse_y = ev->y;
   1376 
   1377 			if ((ev->y) < (dlg->y + DIALOG_TB)
   1378 			    || ev->y >= (dlg->y + DIALOG_TB + ld->n_items)
   1379 			    || (ev->x) < (dlg->x + DIALOG_LB)
   1380 			    || (ev->x) > (dlg->x + dlg->xw - DIALOG_LB))
   1381 				break; /* out of the dialog */
   1382 
   1383 			n = ev->y - dlg->y - DIALOG_TB;
   1384 			for (a = 0; a < n; a++) {
   1385 				struct list *l1;
   1386 				l1 =
   1387 				    next_in_tree(ld, l); /* current item under
   1388 				                            the mouse pointer */
   1389 				if (l1 == ld->list)
   1390 					goto break2;
   1391 				else
   1392 					l = l1;
   1393 			}
   1394 
   1395 			l->type ^= 4;
   1396 			ld->current_pos = l;
   1397 			ld->win_pos = n;
   1398 			rd.n = 0;
   1399 			draw_to_window(dlg->win, redraw_list, &rd);
   1400 			draw_to_window(dlg->win, redraw_list_line,
   1401 			               &rd); /* set cursor */
   1402 			return EVENT_PROCESSED;
   1403 		}
   1404 		/* click on item */
   1405 		if (((ev->b & BM_ACT) == B_DOWN || (ev->b & BM_ACT) == B_DRAG)
   1406 		    && (ev->b & BM_BUTT) == B_LEFT) {
   1407 			int n, a;
   1408 			struct list *l = ld->win_offset;
   1409 
   1410 			last_mouse_y = ev->y;
   1411 
   1412 			if ((ev->y) < (dlg->y + DIALOG_TB)
   1413 			    || ev->y >= (dlg->y + DIALOG_TB + ld->n_items)
   1414 			    || (ev->x) < (dlg->x + DIALOG_LB)
   1415 			    || (ev->x) > (dlg->x + dlg->xw - DIALOG_LB))
   1416 				goto skip_item_click; /* out of the dialog */
   1417 
   1418 			n = ev->y - dlg->y - DIALOG_TB;
   1419 			for (a = 0; a < n; a++) {
   1420 				struct list *l1;
   1421 				l1 =
   1422 				    next_in_tree(ld, l); /* current item under
   1423 				                            the mouse pointer */
   1424 				if (l1 == ld->list) {
   1425 					n = a;
   1426 					break;
   1427 				} else
   1428 					l = l1;
   1429 			}
   1430 			a = ld->type ? ((l->depth) >= 0 ? (l->depth) + 1 : 0)
   1431 			             : (l->depth >= 0);
   1432 
   1433 			ld->current_pos = l;
   1434 
   1435 			/* clicked on directory graphical stuff */
   1436 			if ((ev->b & BM_ACT) == B_DOWN && ld->type
   1437 			    && ev->x < (dlg->x + DIALOG_LB
   1438 			                + a * BFU_ELEMENT_WIDTH)
   1439 			    && (l->type & 1)) {
   1440 				l->type ^= 2;
   1441 				if (!(l->type & 2))
   1442 					unselect_in_folder(ld, ld->current_pos);
   1443 			}
   1444 			ld->win_pos = n;
   1445 			rd.n = 0;
   1446 			draw_to_window(dlg->win, redraw_list, &rd);
   1447 			draw_to_window(dlg->win, redraw_list_line,
   1448 			               &rd); /* set cursor */
   1449 			return EVENT_PROCESSED;
   1450 		}
   1451 		/* scroll with the bar */
   1452 skip_item_click:
   1453 		if ((ev->b & BM_ACT) == B_DRAG
   1454 		    && (ev->b & BM_BUTT) == B_MIDDLE) {
   1455 			long delta = ev->y - last_mouse_y;
   1456 
   1457 			last_mouse_y = ev->y;
   1458 			if (delta > 0) /* scroll down */
   1459 			{
   1460 				if (next_in_tree(ld, ld->current_pos)
   1461 				    == ld->list)
   1462 					return EVENT_PROCESSED; /* already at
   1463 					                           the bottom */
   1464 				ld->current_pos =
   1465 				    next_in_tree(ld, ld->current_pos);
   1466 				ld->win_pos++;
   1467 				rd.n = -1;
   1468 				if (ld->win_pos
   1469 				    > ld->n_items - 1) /* scroll down */
   1470 				{
   1471 					ld->win_pos = ld->n_items - 1;
   1472 					ld->win_offset =
   1473 					    next_in_tree(ld, ld->win_offset);
   1474 					draw_to_window(dlg->win, scroll_list,
   1475 					               &rd);
   1476 				}
   1477 				draw_to_window(dlg->win, redraw_list_line, &rd);
   1478 			}
   1479 			if (delta < 0) /* scroll up */
   1480 			{
   1481 				if (ld->current_pos == ld->list)
   1482 					return EVENT_PROCESSED; /* already on
   1483 					                           the top */
   1484 				ld->current_pos =
   1485 				    prev_in_tree(ld, ld->current_pos);
   1486 				ld->win_pos--;
   1487 				rd.n = +1;
   1488 				if (ld->win_pos < 0) /* scroll up */
   1489 				{
   1490 					ld->win_pos = 0;
   1491 					ld->win_offset =
   1492 					    prev_in_tree(ld, ld->win_offset);
   1493 					draw_to_window(dlg->win, scroll_list,
   1494 					               &rd);
   1495 				}
   1496 				draw_to_window(dlg->win, redraw_list_line, &rd);
   1497 			}
   1498 			return EVENT_PROCESSED;
   1499 		}
   1500 		/* mouse wheel */
   1501 		if ((ev->b & BM_ACT) == B_MOVE
   1502 		    && ((ev->b & BM_BUTT) == B_WHEELUP
   1503 		        || (ev->b & BM_BUTT) == B_WHEELDOWN
   1504 		        || (ev->b & BM_BUTT) == B_WHEELDOWN1
   1505 		        || (ev->b & BM_BUTT) == B_WHEELUP1)) {
   1506 			int button = (int)ev->b & BM_BUTT;
   1507 			last_mouse_y = ev->y;
   1508 
   1509 			if (button == B_WHEELDOWN
   1510 			    || button == B_WHEELDOWN1) /* scroll down */
   1511 			{
   1512 				if (next_in_tree(ld, ld->current_pos)
   1513 				    == ld->list)
   1514 					return EVENT_PROCESSED; /* already at
   1515 					                           the bottom */
   1516 				ld->current_pos =
   1517 				    next_in_tree(ld, ld->current_pos);
   1518 				ld->win_pos++;
   1519 				rd.n = -1;
   1520 				if (ld->win_pos
   1521 				    > ld->n_items - 1) /* scroll down */
   1522 				{
   1523 					ld->win_pos = ld->n_items - 1;
   1524 					ld->win_offset =
   1525 					    next_in_tree(ld, ld->win_offset);
   1526 					draw_to_window(dlg->win, scroll_list,
   1527 					               &rd);
   1528 				}
   1529 				draw_to_window(dlg->win, redraw_list_line, &rd);
   1530 			}
   1531 			if (button == B_WHEELUP
   1532 			    || button == B_WHEELUP1) /* scroll up */
   1533 			{
   1534 				if (ld->current_pos == ld->list)
   1535 					return EVENT_PROCESSED; /* already on
   1536 					                           the top */
   1537 				ld->current_pos =
   1538 				    prev_in_tree(ld, ld->current_pos);
   1539 				ld->win_pos--;
   1540 				rd.n = +1;
   1541 				if (ld->win_pos < 0) /* scroll up */
   1542 				{
   1543 					ld->win_pos = 0;
   1544 					ld->win_offset =
   1545 					    prev_in_tree(ld, ld->win_offset);
   1546 					draw_to_window(dlg->win, scroll_list,
   1547 					               &rd);
   1548 				}
   1549 				draw_to_window(dlg->win, redraw_list_line, &rd);
   1550 			}
   1551 			return EVENT_PROCESSED;
   1552 		}
   1553 break2:
   1554 		break;
   1555 
   1556 	case EV_INIT:
   1557 	case EV_RESIZE:
   1558 	case EV_REDRAW:
   1559 	case EV_ABORT:
   1560 		break;
   1561 
   1562 	default:
   1563 		break;
   1564 	}
   1565 	return EVENT_NOT_PROCESSED;
   1566 }
   1567 
   1568 /* display function for the list window */
   1569 static void
   1570 create_list_window_fn(struct dialog_data *dlg)
   1571 {
   1572 	struct terminal *term = dlg->win->term;
   1573 	struct list_description *ld =
   1574 	    (struct list_description *)(dlg->dlg->udata2);
   1575 	int min = 0;
   1576 	int w, rw, y;
   1577 	int n_items;
   1578 	struct redraw_data rd;
   1579 
   1580 	int a = 6;
   1581 
   1582 	ld->dlg = dlg;
   1583 	if (ld->button_fn)
   1584 		a++; /* user button */
   1585 	if (ld->type == 1)
   1586 		a++; /* add directory button */
   1587 
   1588 	y = 0;
   1589 	min_buttons_width(term, dlg->items, a, &min);
   1590 
   1591 	w = term->x * 19 / 20 - 2 * DIALOG_LB;
   1592 	if (w < min)
   1593 		w = min;
   1594 	if (w > term->x - 2 * DIALOG_LB)
   1595 		w = term->x - 2 * DIALOG_LB;
   1596 	if (w < 5)
   1597 		w = 5;
   1598 
   1599 	rw = 0;
   1600 	dlg_format_buttons(dlg, NULL, dlg->items, a, 0, &y, w, &rw, AL_CENTER);
   1601 
   1602 	n_items = term->y - y;
   1603 	n_items -= 2 * DIALOG_TB + 2;
   1604 	if (n_items < 2)
   1605 		n_items = 2;
   1606 	ld->n_items = n_items;
   1607 
   1608 	while (ld->win_pos > ld->n_items - 1) {
   1609 		ld->current_pos = prev_in_tree(ld, ld->current_pos);
   1610 		ld->win_pos--;
   1611 	}
   1612 
   1613 	y += ld->n_items;
   1614 
   1615 	rw = w;
   1616 	dlg->xw = rw + 2 * DIALOG_LB;
   1617 	dlg->yw = y + 2 * DIALOG_TB;
   1618 	center_dlg(dlg);
   1619 	draw_dlg(dlg);
   1620 
   1621 	rd.ld = ld;
   1622 	rd.dlg = dlg;
   1623 	rd.n = 0;
   1624 
   1625 	draw_to_window(dlg->win, redraw_list, &rd);
   1626 
   1627 	y = dlg->y + DIALOG_TB + ld->n_items + 1;
   1628 	dlg_format_buttons(dlg, term, dlg->items, a, dlg->x + DIALOG_LB, &y, w,
   1629 	                   &rw, AL_CENTER);
   1630 }
   1631 
   1632 static void
   1633 close_list_window(struct dialog_data *dlg)
   1634 {
   1635 	struct dialog *d = dlg->dlg;
   1636 	struct list_description *ld = (struct list_description *)(d->udata2);
   1637 
   1638 	ld->open = 0;
   1639 	ld->dlg = NULL;
   1640 	free(ld->search_word);
   1641 	ld->search_word = NULL;
   1642 	if (ld->save)
   1643 		ld->save(d->udata);
   1644 }
   1645 
   1646 int
   1647 test_list_window_in_use(struct list_description *ld, struct terminal *term)
   1648 {
   1649 	if (ld->open) {
   1650 		if (term)
   1651 			msg_box(term, NULL, TEXT_(T_INFO), AL_CENTER,
   1652 			        TEXT_(ld->already_in_use), MSG_BOX_END, NULL, 1,
   1653 			        TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
   1654 		return 1;
   1655 	}
   1656 	return 0;
   1657 }
   1658 
   1659 /* dialog->udata2  ...  list_description  */
   1660 /* dialog->udata   ...  session */
   1661 int
   1662 create_list_window(struct list_description *ld, struct list *list,
   1663                    struct terminal *term, struct session *ses)
   1664 {
   1665 	struct dialog *d;
   1666 	int a;
   1667 
   1668 	/* zavodime, zavodime... */
   1669 	if (test_list_window_in_use(ld, term))
   1670 		return 1;
   1671 	ld->open = 1;
   1672 
   1673 	if (!ld->current_pos) {
   1674 		ld->current_pos = list;
   1675 		ld->win_offset = list;
   1676 		ld->win_pos = 0;
   1677 		ld->dlg = NULL;
   1678 	}
   1679 
   1680 	a = 7;
   1681 	if (ld->button_fn)
   1682 		a++;
   1683 	if (ld->type == 1)
   1684 		a++;
   1685 
   1686 	d = mem_calloc(sizeof(struct dialog) + a * sizeof(struct dialog_item));
   1687 
   1688 	d->title = TEXT_(ld->window_title);
   1689 	d->fn = create_list_window_fn;
   1690 	d->abort = close_list_window;
   1691 	d->handle_event = list_event_handler;
   1692 	d->udata = ses;
   1693 	d->udata2 = ld;
   1694 
   1695 	a = 0;
   1696 
   1697 	if (ld->button_fn) {
   1698 		d->items[a].type = D_BUTTON;
   1699 		d->items[a].fn = list_item_button;
   1700 		d->items[a].text = TEXT_(ld->button);
   1701 		a++;
   1702 	}
   1703 
   1704 	if (ld->type == 1) {
   1705 		d->items[a].type = D_BUTTON;
   1706 		d->items[a].text = TEXT_(T_FOLDER);
   1707 		d->items[a].fn = list_folder_add;
   1708 		a++;
   1709 	}
   1710 
   1711 	d->items[a].type = D_BUTTON;
   1712 	d->items[a].text = TEXT_(T_ADD);
   1713 	d->items[a].fn = list_item_add;
   1714 
   1715 	d->items[a + 1].type = D_BUTTON;
   1716 	d->items[a + 1].text = TEXT_(T_DELETE);
   1717 	d->items[a + 1].fn = list_item_delete;
   1718 
   1719 	d->items[a + 2].type = D_BUTTON;
   1720 	d->items[a + 2].text = TEXT_(T_EDIT);
   1721 	d->items[a + 2].fn = list_item_edit;
   1722 
   1723 	d->items[a + 3].type = D_BUTTON;
   1724 	d->items[a + 3].text = TEXT_(T_MOVE);
   1725 	d->items[a + 3].fn = list_item_move;
   1726 
   1727 	d->items[a + 4].type = D_BUTTON;
   1728 	d->items[a + 4].text = TEXT_(T_UNSELECT_ALL);
   1729 	d->items[a + 4].fn = list_item_unselect;
   1730 
   1731 	d->items[a + 5].type = D_BUTTON;
   1732 	d->items[a + 5].gid = B_ESC;
   1733 	d->items[a + 5].fn = cancel_dialog;
   1734 	d->items[a + 5].text = TEXT_(T_CLOSE);
   1735 
   1736 	d->items[a + 6].type = D_END;
   1737 	do_dialog(term, d, getml(d, NULL));
   1738 	return 0;
   1739 }
   1740 
   1741 void
   1742 reinit_list_window(struct list_description *ld)
   1743 {
   1744 	ld->current_pos = ld->list;
   1745 	ld->win_offset = ld->list;
   1746 	ld->win_pos = 0;
   1747 
   1748 	if (ld->open)
   1749 		internal("reinit_list_window: calling reinit while open");
   1750 }