links/listedit.c

1933 lines
58 KiB
C

/* listedit.c
* (c) 2002 Petr 'Brain' Kulhavy
* This file is a part of the Links program, released under GPL.
*/
#include "links.h"
/*
(#####)
(#########)
)))) (######)
,C--O (###) _________________
|`:, \ ~~ |. , v , .|
`-__o ~~ ; | ZAKAZ KOURENI |
/ _ \ ` |. .|
| ( \__`_=k== `~~~~~~~~~~~~~~~~~'
| `-/__---'
|=====C/
`. ),'
\ /
||_|
|| |__
||____)
v v v , ,v
KONECNE NEJAKE KOMENTARE...
*/
/* Klikani myssi (nebo mysijou, jak rika Mikulas) v list-okne:
* (hrozne dulezity komentar ;-P )
*
* Klikani je vyreseno nasledovne, pokud ma nekdo lepsi napad, nebo nejake
* vyhrady, tak at mi je posle.
*
* Prostredni tlacitko+pohyb je scroll nahoru/dolu. Levym tlacitkem se nastavi
* kurzor (cerna lista) na konkretni polozku. Kdyz se levym klikne na adresar
* (na ty graficke nesmysly, ne na ten text), tak se adresar toggle
* otevre/zavre. Prave tlacitko oznaci/odznaci polozku/adresar.
*/
/* Premistovani polozek:
*
* Pravym tlacitkem se oznaci/odznaci polozka. Cudlikem "Odznacit vse" se
* vsechny polozky odznaci. Cudlik "Prestehovat" presune vsechny oznacene
* polozky za aktualni pozici, v tom poradi, jak jsou v seznamu. Pri zavreni
* adresare se vsechny polozky v adresari odznaci.
*/
/* Prekreslovani grafickych nesmyslu v okenku je samozrejme bez jedineho
* v
* bliknuti. Ne jako nejmenovane browsery... Proste obraz jako BIC (TM)
*/
/* Ovladani klavesnici:
* sipky, page up, page down, home end pohyb
* + otevri adresar
* - zavri adresar
* mezera toggle adresar
* ins, *, 8, i toggle oznacit
* ?, /, N, n hledani nahoru, dolu, znova, znova na druhou stranu
*/
/*
* Struktura struct list_decription obsahuje popis seznamu. Tenhle file
* obsahuje obecne funkce k obsluze seznamu. Pomoci struct list_description se
* seznam customizuje. Obecne funkce volaji funkce z list_description.
*
* Jedina funkce z tohoto filu, ktera se vola zvenku, je create_list_window. Ta
* vyrobi a obstarava okno pro obsluhu seznamu.
*
* Obecny list neresi veci jako nahravani seznamu z filu, ukladani na disk
* atd.(tyhle funkce si uzivatel musi napsat sam). Resi vlastne jenom to velke
* okno na manipulaci se seznamem.
*/
/*
* Aby bylo konzistentni pridavani a editovani polozek, tak se musi pytlacit.
*
* To znamena, ze pri pridavani polozky do listu se vyrobi nova polozka
* (NEPRIDA se do seznamu), pusti se edit a po zmacknuti OK se polozka prida do
* seznamu. Pri zmacknuti cancel, se polozka smaze.
*
* Pri editovani polozky se vyrobi nova polozka, zkopiruje se do ni obsah te
* puvodni (od toho tam je funkce copy_item), pak se zavola edit a podobne jako
* v predchozim pripade: pri cancel se polozka smaze, pri OK se zkopiruje do
* puvodni polozky a smaze se taky.
*
* O smazani polozky pri cancelu se bude starat uzivatelska funkce edit_item
* sama. Funkce edit_item po zmacknuti OK zavola funkci, kterou dostane. Jako
* 1. argument ji da data, ktera dostane, jako 2. argument ji preda pointer na
* item.
*/
/*
* Seznam je definovan ve struct list. Muze byt bud placaty nebo stromovy.
*
* K placatemu asi neni co dodat. U placateho se ignoruje hloubka a neexistuji
* adresare - vsechny polozky jsou si rovny (typ polozky se ignoruje).
*
* Stromovy seznam:
* Kazdy clen seznamu ma flag sbaleno/rozbaleno. U itemy se to ignoruje, u
* adresare to znamena, zda se zobrazuje obsah nebo ne. Aby rozbaleno/sbaleno
* bylo u vsech prvku adresare, to by neslo: kdybych mel adresar a v nem dalsi
* adresar, tak bych u toho vnoreneho adresare nevedel, jestli to
* sbaleno/rozbaleno je od toho vnoreneho adresare, nebo od toho nad nim.
*
* Clenove seznamu maji hloubku - cislo od 0 vyse. Cim je prvek hloubeji ve
* strukture, tim je hloubka vyssi. Obsah adresare s hloubkou x je souvisly blok
* nasledujicich prvku s hloubkou >x.
*
* Hlava ma hloubku -1 a zobrazuje se taky jako clen seznamu (aby se dal
* zobrazit prazdny seznam a dalo se do nej pridavat), takze se da vlastne cely
* seznam zabalit/rozbalit. Hlava sice nema data, ale funkce type_item ji musi
* umet zobrazit. Jako popis bude psat fixni text, napriklad "Bookmarks".
*
* Pro urychleni vykreslovani kazdy prvek v seznamu s adresarema obsahuje
* pointer na otce (polozka fotr). U plocheho seznamu se tento pointer
* ignoruje.
*
* Strukturu stromu bude vykreslovat obecna funkce (v tomto filu), protoze v
* obecnem listu je struktura uz zachycena.
*/
/*
* V hlavnim okne se da nadefinovat 1 uzivatelske tlacitko. Polozka button ve
* struct list_description obsahuje popisku tlacitka (kod stringu v
* prekodovavacich tabulkach). Funkce button_fn je zavolana pri stisku
* tlacitka, jako argument (void *) dostane aktualni polozku. Pokud je
* button_fn NULL, tlacitko se nekona.
*
* Toto tlacitko se da vyuzit napriklad u bookmarku, kde je potreba [ Goto ].
*
* Kdyz bude potreba predavat obsluzne funkci tlacitka nejake dalsi argumenty,
* tak se pripadne definice obsluzne funkce prepise.
*
* Tlacitko funguje jen na polozky. Nefunguje na adresare (pokud se jedna o
* stromovy list) ani na hlavu.
*/
/* Jak funguje default_value:
* kdyz se zmackne tlacitko add, zavola se funkce default_value, ktera si
* naalokuje nejaky data pro new_item. Do funkce default_value se treba u
* bookmarku umisti okopirovani altualniho nazvu a url stranky. Pak se zavola
* new_item, ktera si prislusne hodnoty dekoduje a pomoci nich vyplni novou
* polozku. Funkce new_item pak MUSI data dealokovat. Pokud funkce new_item
* dostane misto pointeru s daty NULL, vyrobi prazdnou polozku.
*
* Default value musi vratit hodnoty v kodovani uvedenem v list_description
*/
/* Pristupovani z vice linksu:
*
* ... se neresi - je zakazano. K tomu slouzi polozka open ve struct
* list_description, ktera rika, jestli je okno uz otevrene, nebo ne.
*/
/* Prekodovavani znakovych sad:
*
* type_item vraci text prekodovany do kodovani terminalu, ktery dostane.
*/
/* struct list *current_pos; current cursor position in the list */
/* struct list *win_offset; item at the top of the window */
/* int win_pos; current y position in the window */
#define BOHNICE "+420-2-84016111"
#define BFU_ELEMENT_EMPTY 0
#define BFU_ELEMENT_PIPE 1
#define BFU_ELEMENT_L 2
#define BFU_ELEMENT_TEE 3
#define BFU_ELEMENT_CLOSED 4
#define BFU_ELEMENT_CLOSED_DOWN 5
#define BFU_ELEMENT_OPEN 6
#define BFU_ELEMENT_OPEN_DOWN 7
/* for mouse scrolling */
static long last_mouse_y;
static void list_edit_toggle(struct dialog_data *dlg, struct list_description *ld);
#ifdef G
#define sirka_scrollovadla (G_SCROLL_BAR_WIDTH<<1)
#else
#define sirka_scrollovadla 0
#endif
/* This function uses these defines from setup.h:
*
* BFU_GRX_WIDTH
* BFU_GRX_HEIGHT
* BFU_ELEMENT_WIDTH
*/
/* draws one of BFU elements: | |- [-] [+] */
/* BFU elements are used in the list window */
/* this function also defines shape and size of the elements */
/* returns width of the BFU element (all elements have the same size, but sizes differ if we're in text mode or in graphics mode) */
static int draw_bfu_element(struct terminal * term, int x, int y, unsigned char c, long b, long f, unsigned char type, unsigned char selected)
{
#ifdef G
if (!F){
#endif
unsigned char vertical=179;
unsigned char horizontal=196;
unsigned char tee=195;
unsigned char l=192;
switch (type)
{
case BFU_ELEMENT_EMPTY:
c|=ATTR_FRAME;
set_char(term,x,y,' ',c);
set_char(term,x+1,y,' ',c);
set_char(term,x+2,y,' ',c);
set_char(term,x+3,y,' ',c);
set_char(term,x+4,y,' ',c);
break;
case BFU_ELEMENT_PIPE:
c|=ATTR_FRAME;
set_char(term,x,y,' ',c);
set_char(term,x+1,y,vertical,c);
set_char(term,x+2,y,' ',c);
set_char(term,x+3,y,' ',c);
set_char(term,x+4,y,' ',c);
break;
case BFU_ELEMENT_L:
c|=ATTR_FRAME;
set_char(term,x,y,' ',c);
set_char(term,x+1,y,l,c);
set_char(term,x+2,y,horizontal,c);
set_char(term,x+3,y,horizontal,c);
set_char(term,x+4,y,' ',c);
break;
case BFU_ELEMENT_TEE:
c|=ATTR_FRAME;
set_char(term,x,y,' ',c);
set_char(term,x+1,y,tee,c);
set_char(term,x+2,y,horizontal,c);
set_char(term,x+3,y,horizontal,c);
set_char(term,x+4,y,' ',c);
break;
case BFU_ELEMENT_CLOSED:
case BFU_ELEMENT_CLOSED_DOWN:
set_char(term,x,y,'[',c);
set_char(term,x+1,y,'+',c);
set_char(term,x+2,y,']',c);
c|=ATTR_FRAME;
set_char(term,x+3,y,horizontal,c);
set_char(term,x+4,y,' ',c);
break;
case BFU_ELEMENT_OPEN:
case BFU_ELEMENT_OPEN_DOWN:
set_char(term,x,y,'[',c);
set_char(term,x+1,y,'-',c);
set_char(term,x+2,y,']',c);
c|=ATTR_FRAME;
set_char(term,x+3,y,horizontal,c);
set_char(term,x+4,y,' ',c);
break;
default:
internal_error("draw_bfu_element: unknown BFU element type %d.\n",type);
}
if (selected)set_char(term,x+4,y,'*',c);
return BFU_ELEMENT_WIDTH; /* BFU element size in text mode */
#ifdef G
}else{
struct graphics_device *dev=term->dev;
struct rect r;
restrict_clip_area(dev,&r,x,y,x+5*BFU_GRX_WIDTH,y+BFU_GRX_HEIGHT);
switch (type)
{
case BFU_ELEMENT_EMPTY:
drv->fill_area(dev,x,y,x+4*BFU_GRX_WIDTH,y+BFU_GRX_HEIGHT,b);
break;
case BFU_ELEMENT_PIPE:
/* pipe */
drv->draw_vline(dev,x+1*BFU_GRX_WIDTH,y,y+BFU_GRX_HEIGHT,f);
drv->draw_vline(dev,x+1+1*BFU_GRX_WIDTH,y,y+BFU_GRX_HEIGHT,f);
/* clear the rest */
drv->fill_area(dev,x,y,x+1*BFU_GRX_WIDTH,y+BFU_GRX_HEIGHT,b);
drv->fill_area(dev,x+2+1*BFU_GRX_WIDTH,y,x+4*BFU_GRX_WIDTH,y+BFU_GRX_HEIGHT,b);
break;
case BFU_ELEMENT_L:
/* l */
drv->draw_vline(dev,x+1*BFU_GRX_WIDTH,y,y+(int)(.5*BFU_GRX_HEIGHT),f);
drv->draw_vline(dev,x+1+1*BFU_GRX_WIDTH,y,y+(int)(.5*BFU_GRX_HEIGHT),f);
drv->draw_hline(dev,x+1*BFU_GRX_WIDTH,y+(int)(.5*BFU_GRX_HEIGHT),x+1+(int)(3.5*BFU_GRX_WIDTH),f);
drv->draw_hline(dev,x+1*BFU_GRX_WIDTH,y-1+(int)(.5*BFU_GRX_HEIGHT),x+1+(int)(3.5*BFU_GRX_WIDTH),f);
/* clear the rest */
drv->fill_area(dev,x,y,x+1*BFU_GRX_WIDTH,y+BFU_GRX_HEIGHT,b);
drv->fill_area(dev,x+BFU_GRX_WIDTH,y+(int)(.5*BFU_GRX_HEIGHT)+1,x+1+(int)(3.5*BFU_GRX_WIDTH),y+BFU_GRX_HEIGHT,b);
drv->fill_area(dev,x+2+BFU_GRX_WIDTH,y,x+1+(int)(3.5*BFU_GRX_WIDTH),y-1+(int)(.5*BFU_GRX_HEIGHT),b);
drv->fill_area(dev,x+1+(int)(3.5*BFU_GRX_WIDTH),y,x+4*BFU_GRX_WIDTH,y+BFU_GRX_HEIGHT,b);
break;
case BFU_ELEMENT_TEE:
/* tee */
drv->draw_vline(dev,x+1*BFU_GRX_WIDTH,y,y+BFU_GRX_HEIGHT,f);
drv->draw_vline(dev,x+1+1*BFU_GRX_WIDTH,y,y+BFU_GRX_HEIGHT,f);
drv->draw_hline(dev,x+1*BFU_GRX_WIDTH,y+(int)(.5*BFU_GRX_HEIGHT),x+1+(int)(3.5*BFU_GRX_WIDTH),f);
drv->draw_hline(dev,x+1*BFU_GRX_WIDTH,y-1+(int)(.5*BFU_GRX_HEIGHT),x+1+(int)(3.5*BFU_GRX_WIDTH),f);
/* clear the rest */
drv->fill_area(dev,x,y,x+1*BFU_GRX_WIDTH,y+BFU_GRX_HEIGHT,b);
drv->fill_area(dev,x+2+BFU_GRX_WIDTH,y+(int)(.5*BFU_GRX_HEIGHT)+1,x+1+(int)(3.5*BFU_GRX_WIDTH),y+BFU_GRX_HEIGHT,b);
drv->fill_area(dev,x+2+BFU_GRX_WIDTH,y,x+1+(int)(3.5*BFU_GRX_WIDTH),y-1+(int)(.5*BFU_GRX_HEIGHT),b);
drv->fill_area(dev,x+1+(int)(3.5*BFU_GRX_WIDTH),y,x+4*BFU_GRX_WIDTH,y+BFU_GRX_HEIGHT,b);
break;
case BFU_ELEMENT_CLOSED:
case BFU_ELEMENT_CLOSED_DOWN:
/* vertical line of the + */
drv->draw_vline(dev,x+1*BFU_GRX_WIDTH,y+1+(int)(.25*BFU_GRX_HEIGHT),y-1+(int)(.75*BFU_GRX_HEIGHT),f);
drv->draw_vline(dev,x+1+1*BFU_GRX_WIDTH,y+1+(int)(.25*BFU_GRX_HEIGHT),y-1+(int)(.75*BFU_GRX_HEIGHT),f);
/* clear around the + */
drv->fill_area(dev,x+2+(int)(.5*BFU_GRX_WIDTH),y+3,x+(int)(1.5*BFU_GRX_WIDTH),y+1+(int)(.25*BFU_GRX_HEIGHT),b);
drv->fill_area(dev,x+2+(int)(.5*BFU_GRX_WIDTH),y-1+(int)(.75*BFU_GRX_HEIGHT),x+(int)(1.5*BFU_GRX_WIDTH),y-3+BFU_GRX_HEIGHT,b);
drv->fill_area(dev,x+2+(int)(.5*BFU_GRX_WIDTH),y+1+(int)(.25*BFU_GRX_HEIGHT),x+BFU_GRX_WIDTH,y-1+(int)(.5*BFU_GRX_HEIGHT),b);
drv->fill_area(dev,x+2+BFU_GRX_WIDTH,y+1+(int)(.25*BFU_GRX_HEIGHT),x+(int)(1.5*BFU_GRX_WIDTH),y-1+(int)(.5*BFU_GRX_HEIGHT),b);
drv->fill_area(dev,x+2+(int)(.5*BFU_GRX_WIDTH),y+1+(int)(.5*BFU_GRX_HEIGHT),x+BFU_GRX_WIDTH,y-3+BFU_GRX_HEIGHT,b);
drv->fill_area(dev,x+2+BFU_GRX_WIDTH,y+1+(int)(.5*BFU_GRX_HEIGHT),x+(int)(1.5*BFU_GRX_WIDTH),y-3+BFU_GRX_HEIGHT,b);
/*-fallthrough*/
case BFU_ELEMENT_OPEN:
case BFU_ELEMENT_OPEN_DOWN:
/* box */
drv->draw_vline(dev,x+2,y+1,y-1+BFU_GRX_HEIGHT,f);
drv->draw_vline(dev,x+3,y+1,y-1+BFU_GRX_HEIGHT,f);
drv->draw_vline(dev,x-1+2*BFU_GRX_WIDTH,y+1,y-1+BFU_GRX_HEIGHT,f);
drv->draw_vline(dev,x-2+2*BFU_GRX_WIDTH,y+1,y-1+BFU_GRX_HEIGHT,f);
drv->draw_hline(dev,x+4,y+1,x-2+2*BFU_GRX_WIDTH,f);
drv->draw_hline(dev,x+4,y+2,x-2+2*BFU_GRX_WIDTH,f);
drv->draw_hline(dev,x+4,y-2+BFU_GRX_HEIGHT,x-2+2*BFU_GRX_WIDTH,f);
drv->draw_hline(dev,x+4,y-3+BFU_GRX_HEIGHT,x-2+2*BFU_GRX_WIDTH,f);
/* horizontal line of the - */
drv->draw_hline(dev,x+2+(int)(.5*BFU_GRX_WIDTH),y+(int)(.5*BFU_GRX_HEIGHT),x+(int)(1.5*BFU_GRX_WIDTH),f);
drv->draw_hline(dev,x+2+(int)(.5*BFU_GRX_WIDTH),y-1+(int)(.5*BFU_GRX_HEIGHT),x+(int)(1.5*BFU_GRX_WIDTH),f);
/* line to title */
drv->draw_hline(dev,x+2*BFU_GRX_WIDTH,y+(BFU_GRX_HEIGHT>>1),x+1+(int)(3.5*BFU_GRX_WIDTH),f);
drv->draw_hline(dev,x+2*BFU_GRX_WIDTH,y-1+(BFU_GRX_HEIGHT>>1),x+1+(int)(3.5*BFU_GRX_WIDTH),f);
/* top and bottom short vertical line */
drv->draw_hline(dev,x+1*BFU_GRX_WIDTH,y,x+2+1*BFU_GRX_WIDTH,f);
drv->draw_hline(dev,x+1*BFU_GRX_WIDTH,y-1+BFU_GRX_HEIGHT,x+2+1*BFU_GRX_WIDTH,type == BFU_ELEMENT_OPEN || type == BFU_ELEMENT_CLOSED ? b : f);
/* clear the rest */
drv->draw_vline(dev,x,y,y+BFU_GRX_HEIGHT,b);
drv->draw_vline(dev,x+1,y,y+BFU_GRX_HEIGHT,b);
drv->draw_hline(dev,x+2,y,x+BFU_GRX_WIDTH,b);
drv->draw_hline(dev,x+2,y-1+BFU_GRX_HEIGHT,x+BFU_GRX_WIDTH,b);
drv->draw_hline(dev,x+2+BFU_GRX_WIDTH,y,x+2*BFU_GRX_WIDTH,b);
drv->draw_hline(dev,x+2+BFU_GRX_WIDTH,y-1+BFU_GRX_HEIGHT,x+2*BFU_GRX_WIDTH,b);
drv->fill_area(dev,x+2*BFU_GRX_WIDTH,y,x+1+(int)(3.5*BFU_GRX_WIDTH),y+(int)(.5*BFU_GRX_HEIGHT)-1,b);
drv->fill_area(dev,x+2*BFU_GRX_WIDTH,y+1+(int)(.5*BFU_GRX_HEIGHT),x+1+(int)(3.5*BFU_GRX_WIDTH),y+BFU_GRX_HEIGHT,b);
drv->fill_area(dev,x+4,y+3,x+2+(int)(.5*BFU_GRX_WIDTH),y-3+BFU_GRX_HEIGHT,b);
drv->fill_area(dev,x+(int)(1.5*BFU_GRX_WIDTH),y+3,x-2+2*BFU_GRX_WIDTH,y-3+BFU_GRX_HEIGHT,b);
drv->fill_area(dev,x+2+(int)(.5*BFU_GRX_WIDTH),y+3,x+(int)(1.5*BFU_GRX_WIDTH),y+1+(int)(.25*BFU_GRX_HEIGHT),b);
drv->fill_area(dev,x+2+(int)(.5*BFU_GRX_WIDTH),y-1+(int)(.75*BFU_GRX_HEIGHT),x+(int)(1.5*BFU_GRX_WIDTH),y-3+BFU_GRX_HEIGHT,b);
if (type==BFU_ELEMENT_OPEN || type == BFU_ELEMENT_OPEN_DOWN)
{
drv->fill_area(dev,x+2+(int)(.5*BFU_GRX_WIDTH),y+3,x+(int)(1.5*BFU_GRX_WIDTH),y-1+(int)(.5*BFU_GRX_HEIGHT),b);
drv->fill_area(dev,x+2+(int)(.5*BFU_GRX_WIDTH),y+1+(int)(.5*BFU_GRX_HEIGHT),x+(int)(1.5*BFU_GRX_WIDTH),y-3+BFU_GRX_HEIGHT,b);
}
drv->fill_area(dev,x+1+(int)(3.5*BFU_GRX_WIDTH),y,x+4*BFU_GRX_WIDTH,y+BFU_GRX_HEIGHT,b);
break;
default:
internal_error("draw_bfu_element: unknown BFU element type %d.\n",type);
}
if (!selected)
drv->fill_area(dev,x+4*BFU_GRX_WIDTH,y,x+5*BFU_GRX_WIDTH,y+BFU_GRX_HEIGHT,b);
else
{
drv->fill_area(dev,x+4*BFU_GRX_WIDTH,y,x+(int)(4.25*BFU_GRX_WIDTH),y+BFU_GRX_HEIGHT,b);
drv->fill_area(dev,x+(int)(4.25*BFU_GRX_WIDTH),y,x+(int)(4.75*BFU_GRX_WIDTH),y+(int)(2.5*BFU_GRX_HEIGHT),b);
drv->fill_area(dev,x+(int)(4.25*BFU_GRX_WIDTH),y+(int)(.25*BFU_GRX_HEIGHT),x+(int)(4.75*BFU_GRX_WIDTH),y+(int)(.75*BFU_GRX_HEIGHT),f);
drv->fill_area(dev,x+(int)(4.25*BFU_GRX_WIDTH),y+(int)(.75*BFU_GRX_HEIGHT),x+(int)(4.75*BFU_GRX_WIDTH),y+BFU_GRX_HEIGHT,b);
drv->fill_area(dev,x+(int)(4.75*BFU_GRX_WIDTH),y,x+5*BFU_GRX_WIDTH,y+BFU_GRX_HEIGHT,b);
}
set_clip_area(dev, &r);
return BFU_ELEMENT_WIDTH;
}
#endif
}
/* aux structure for parameter exchange for redrawing list window */
struct redraw_data
{
struct list_description *ld;
struct dialog_data *dlg;
int n;
};
static void redraw_list(struct terminal *term, void *bla);
/* returns next visible item in tree list */
/* works only with visible items (head or any item returned by this function) */
/* when list is flat returns next item */
static struct list *next_in_tree(struct list_description *ld, struct list *item)
{
int depth = item->depth;
/* flat list */
if (!ld->type)
return list_next(item);
if (!(item->type & 1) || (item->type & 2)) /* item or opened folder */
return list_next(item);
/* skip content of this folder */
do item = list_next(item); while (item->depth > depth); /* must stop on head 'cause it's depth is -1 */
return item;
}
/* returns previous visible item in tree list */
/* works only with visible items (head or any item returned by this function) */
/* when list is flat returns previous item */
static struct list *prev_in_tree(struct list_description *ld, struct list *item)
{
struct list *last_closed;
int depth = item->depth;
/* flat list */
if (!ld->type)
return list_prev(item);
if (item == ld->list)
depth=0;
/* items with same or lower depth must be visible, because current item is visible */
if (list_prev(item)->depth <= item->depth)
return list_prev(item);
/* find item followed with opened fotr's only */
/* searched item is last closed folder (going up from item) or item->prev */
last_closed = list_prev(item);
item = list_prev(item);
while (1) {
if ((item->type & 3) == 1) /* closed folder */
last_closed = item;
if (item->depth <= depth)
break;
item = item->fotr;
}
return last_closed;
}
#ifdef G
static int get_total_items(struct list_description *ld)
{
struct list *l = ld->list;
int count = 0;
do{
l = next_in_tree(ld, l);
count++;
} while (l != ld->list);
return count;
}
static int get_scroll_pos(struct list_description *ld)
{
struct list *l;
int count = 0;
for (l = ld->list; l != ld->win_offset; l = next_in_tree(ld,l))
count++;
return count;
}
static int get_visible_items(struct list_description *ld)
{
struct list *l = ld->win_offset;
int count = 0;
do{
l = next_in_tree(ld,l);
count++;
} while (count < ld->n_items && l != ld->list);
return count;
}
static struct list *find_last_in_window(struct list_description *ld)
{
struct list *l = ld->win_offset;
struct list *x = l;
int count = 0;
do{
l = next_in_tree(ld, l);
count++;
if (l != ld->list && count != ld->n_items)
x = l;
} while (count < ld->n_items && l != ld->list);
return x;
}
#endif
static int get_win_pos(struct list_description *ld)
{
struct list *l;
int count = 0;
for (l = ld->win_offset; l != ld->current_pos; l = next_in_tree(ld, l))
count++;
return count;
}
static void unselect_in_folder(struct list_description *ld, struct list *l)
{
int depth;
depth = l->depth;
for (l = list_next(l); l != ld->list && l->depth > depth; l = list_next(l))
l->type &= ~4;
}
/* aux function for list_item_add */
static void list_insert_behind_item(struct dialog_data *dlg, struct list *pos, struct list *item, struct list_description *ld)
{
struct list *a;
struct redraw_data rd;
/* BEFORE: ... <----> pos <--(possible invisible items)--> a <----> ... */
/* AFTER: ... <----> pos <--(possible invisible items)--> item <----> a <----> ... */
a = next_in_tree(ld,pos);
add_before_pos(a, item);
/* if list is flat a->fotr will contain nosenses, but it won't crash ;-) */
if ((pos->type & 3) == 3 || pos->depth == -1) { item->fotr=pos; item->depth=pos->depth+1; } /* open directory or head */
else { item->fotr = pos->fotr; item->depth = pos->depth; }
ld->current_pos = next_in_tree(ld, ld->current_pos); /* ld->current_pos->next==item */
ld->win_pos++;
if (ld->win_pos > ld->n_items - 1) { /* scroll down */
ld->win_pos = ld->n_items - 1;
ld->win_offset = next_in_tree(ld, ld->win_offset);
}
ld->modified = 1;
rd.ld = ld;
rd.dlg = dlg;
rd.n = 0;
draw_to_window(dlg->win, redraw_list, &rd);
}
/* aux function for list_item_edit */
/* copies data of src to dest and calls free on the src */
/* first argument is argument passed to user function */
static void list_copy_item(struct dialog_data *dlg, struct list *dest, struct list *src, struct list_description *ld)
{
struct redraw_data rd;
ld->copy_item(src, dest);
ld->delete_item(src);
ld->modified = 1; /* called after an successful edit */
rd.ld = ld;
rd.dlg = dlg;
rd.n = 0;
draw_to_window(dlg->win, redraw_list, &rd);
}
/* creates new item (calling new_item function) and calls edit_item function */
static int list_item_add(struct dialog_data *dlg, struct dialog_item_data *useless)
{
struct list_description *ld = (struct list_description *)dlg->dlg->udata2;
struct list *item = ld->current_pos;
struct list *new_item;
if (!(new_item = ld->new_item(ld->default_value ? ld->default_value((struct session *)dlg->dlg->udata, 0) : NULL)))
return 1;
new_item->list_entry.prev = NULL;
new_item->list_entry.next = NULL;
new_item->type = 0;
new_item->depth = 0;
ld->edit_item(dlg,new_item, list_insert_behind_item, item, TITLE_ADD);
return 0;
}
/* like list_item_add but creates folder */
static int list_folder_add(struct dialog_data *dlg, struct dialog_item_data *useless)
{
struct list_description *ld = (struct list_description *)dlg->dlg->udata2;
struct list *item = ld->current_pos;
struct list *new_item;
if (!(new_item = ld->new_item(NULL)))
return 1;
new_item->list_entry.prev = NULL;
new_item->list_entry.next = NULL;
new_item->type = 1 | 2;
new_item->depth = 0;
ld->edit_item(dlg, new_item, list_insert_behind_item, item, TITLE_ADD);
return 0;
}
static int list_item_edit(struct dialog_data *dlg,struct dialog_item_data *useless)
{
struct list_description *ld = (struct list_description *)dlg->dlg->udata2;
struct list *item = ld->current_pos;
struct list *new_item;
if (item == ld->list) return 0; /* head */
if (!(new_item = ld->new_item(NULL))) return 1;
new_item->list_entry.prev = NULL;
new_item->list_entry.next = NULL;
ld->copy_item(item, new_item);
ld->edit_item(dlg, new_item, list_copy_item, item, TITLE_EDIT);
return 0;
}
static inline int is_parent(struct list_description *ld, struct list *item, struct list *parent)
{
struct list *l;
if (ld->type)
for (l=item;l->depth>=0;l=l->fotr)
if (l==parent) return 1;
return 0;
}
static int list_item_move(struct dialog_data *dlg, struct dialog_item_data *useless)
{
struct list_description *ld = (struct list_description *)dlg->dlg->udata2;
struct list *i;
struct list *behind = ld->current_pos;
struct redraw_data rd;
int window_moved = 0;
int count = 0;
if (ld->current_pos->type & 4) { /* odznacime current_pos, kdyby nahodou byla oznacena */
count++;
ld->current_pos->type &= ~4;
}
for (i = list_next(ld->list); i != ld->list; ) {
struct list *next = next_in_tree(ld, i);
struct list *prev = list_prev(i);
struct list *behind_next = next_in_tree(ld, behind); /* to se musi pocitat pokazdy, protoze by se nam mohlo stat, ze to je taky oznaceny */
struct list *item_last = list_prev(next); /* last item of moved block */
if (!(i->type & 4)) { i = next; continue; }
if (is_parent(ld, ld->current_pos, i)) { /* we're moving directory into itself - let's behave like item was not selected */
i->type &= ~4;
i = next;
count++;
continue;
}
if ((i->type & 3) == 3) { /* dirty trick */
i->type &= ~2;
next = next_in_tree(ld, i);
prev = list_prev(i);
item_last = list_prev(next);
i->type |= 2;
}
if (i == ld->win_offset) {
window_moved = 1;
if (next != ld->list) ld->win_offset = next;
}
/* upravime fotrisko a hloubku */
if (ld->type) {
int a = i->depth;
struct list *l = i;
if ((behind->type & 3) == 3 || behind == ld->list) { /* open folder or head */
i->fotr = behind;
i->depth = behind->depth + 1;
} else {
i->fotr = behind->fotr;
i->depth = behind->depth;
}
a = i->depth - a;
/* poopravime hloubku v adresari */
while (l != item_last) {
l = list_next(l);
l->depth += a;
}
}
if (behind_next == i) goto predratovano; /* to uz je vsechno, akorat menime hloubku */
/* predratujeme ukazatele kolem bloku na stare pozici */
prev->list_entry.next = &next->list_entry;
next->list_entry.prev = &prev->list_entry;
/* posuneme na novou pozici (tesne pred behind_next) */
i->list_entry.prev = behind_next->list_entry.prev;
behind_next->list_entry.prev->next = &i->list_entry;
item_last->list_entry.next = &behind_next->list_entry;
behind_next->list_entry.prev = &item_last->list_entry;
predratovano:
/* odznacime */
i->type &= ~4;
unselect_in_folder(ld, i);
/* upravime pointery pro dalsi krok */
behind = i;
i = next;
count++;
}
if (window_moved) {
ld->current_pos = ld->win_offset;
ld->win_pos = 0;
} else
ld->win_pos = get_win_pos(ld);
if (!count) {
msg_box(
dlg->win->term, /* terminal */
NULL, /* blocks to free */
TEXT_(T_MOVE), /* title */
AL_CENTER, /* alignment */
TEXT_(T_NO_ITEMS_SELECTED),MSG_BOX_END, /* text */
NULL, /* data */
1, /* # of buttons */
TEXT_(T_CANCEL), msg_box_null, B_ESC|B_ENTER /* button1 */
);
} else {
ld->modified = 1;
rd.ld = ld;
rd.dlg = dlg;
rd.n = 0;
draw_to_window(dlg->win, redraw_list, &rd);
}
return 0;
}
/* unselect all items */
static int list_item_unselect(struct dialog_data *dlg, struct dialog_item_data *useless)
{
struct list_description *ld = (struct list_description *)dlg->dlg->udata2;
struct list *item;
struct redraw_data rd;
item = ld->list;
do {
item->type &= ~4;
item = list_next(item);
} while (item != ld->list);
rd.ld = ld;
rd.dlg = dlg;
rd.n = 0;
draw_to_window(dlg->win, redraw_list, &rd);
return 0;
}
/* user button function - calls button_fn with current item */
static int list_item_button(struct dialog_data *dlg, struct dialog_item_data *useless)
{
struct list_description *ld = (struct list_description *)dlg->dlg->udata2;
struct list *item = ld->current_pos;
struct session *ses = (struct session *)dlg->dlg->udata;
if (!ld->button_fn) internal_error("Links got schizophrenia! Call "BOHNICE".\n");
if (item == ld->list || list_empty(item->list_entry)) return 0; /* head or empty list */
if (ld->type && (item->type & 1)) {
list_edit_toggle(dlg, ld);
return 0; /* this is tree list and item is directory */
}
ld->button_fn(ses,item);
cancel_dialog(dlg, useless);
return 0;
}
struct ve_skodarne_je_jeste_vetsi_narez {
struct list_description *ld;
struct dialog_data *dlg;
struct list *item;
};
/* when delete is confirmed adjusts current_pos and calls delete function */
static void delete_ok(void *data)
{
struct ve_skodarne_je_jeste_vetsi_narez *skd = (struct ve_skodarne_je_jeste_vetsi_narez *)data;
struct list_description *ld = skd->ld;
struct list *item = skd->item;
struct dialog_data *dlg = skd->dlg;
struct redraw_data rd;
/* strong premise: we can't delete head of the list */
if (list_next(ld->current_pos) != ld->list) {
if (ld->current_pos == ld->win_offset) ld->win_offset = list_next(ld->current_pos);
ld->current_pos = list_next(ld->current_pos);
} else { /* last item */
if (!ld->win_pos) /* only one line on the screen */
ld->win_offset = prev_in_tree(ld, ld->win_offset);
else
ld->win_pos--;
ld->current_pos = prev_in_tree(ld, ld->current_pos);
}
ld->delete_item(item);
rd.ld = ld;
rd.dlg = dlg;
rd.n = 0;
ld->modified = 1;
draw_to_window(dlg->win, redraw_list, &rd);
}
/* when delete folder is confirmed adjusts current_pos and calls delete function */
static void delete_folder_recursively(void *data)
{
struct ve_skodarne_je_jeste_vetsi_narez *skd = (struct ve_skodarne_je_jeste_vetsi_narez *)data;
struct list_description *ld = skd->ld;
struct list *item = skd->item;
struct dialog_data *dlg = skd->dlg;
struct redraw_data rd;
struct list *i, *j;
int depth;
for (i = list_next(item), depth = item->depth; i != ld->list && i->depth > depth; ) {
j = i;
i = list_next(i);
ld->delete_item(j);
}
/* strong premise: we can't delete head of the list */
if (list_next(ld->current_pos) != ld->list) {
if (ld->current_pos == ld->win_offset) ld->win_offset = list_next(ld->current_pos);
ld->current_pos = list_next(ld->current_pos);
} else { /* last item */
if (!ld->win_pos) /* only one line on the screen */
ld->win_offset = prev_in_tree(ld, ld->win_offset);
else
ld->win_pos--;
ld->current_pos = prev_in_tree(ld, ld->current_pos);
}
ld->delete_item(item);
rd.ld = ld;
rd.dlg = dlg;
rd.n = 0;
ld->modified = 1;
draw_to_window(dlg->win, redraw_list, &rd);
}
/* tests if directory is emty */
static int is_empty_dir(struct list_description *ld, struct list *dir)
{
if (!ld->type) return 1; /* flat list */
if (!(dir->type & 1)) return 1; /* not a directory */
return list_next(dir)->depth <= dir->depth; /* head depth is -1 */
}
/* delete dialog */
static int list_item_delete(struct dialog_data *dlg,struct dialog_item_data *useless)
{
struct terminal *term=dlg->win->term;
struct list_description *ld=(struct list_description*)(dlg->dlg->udata2);
struct list *item=ld->current_pos;
unsigned char *txt;
struct ve_skodarne_je_jeste_vetsi_narez *narez;
if (item==ld->list||list_empty(item->list_entry))return 0; /* head or empty list */
narez=mem_alloc(sizeof(struct ve_skodarne_je_jeste_vetsi_narez));
narez->ld=ld;narez->item=item;narez->dlg=dlg;
txt=ld->type_item(term, item,0);
if (!txt)
{
txt = stracpy(cast_uchar "");
}
if ((item->type)&1) /* folder */
{
if (!is_empty_dir(ld,item))
msg_box(
term, /* terminal */
getml(txt,narez,NULL), /* blocks to free */
TEXT_(T_DELETE_FOLDER), /* title */
AL_CENTER, /* alignment */
TEXT_(T_FOLDER),cast_uchar " \"",txt,cast_uchar "\" ",TEXT_(T_NOT_EMPTY_SURE_DELETE),MSG_BOX_END, /* text */
(void *)narez, /* data for ld->delete_item */
2, /* # of buttons */
TEXT_(T_NO),msg_box_null,B_ESC, /* button1 */
TEXT_(T_YES),delete_folder_recursively,B_ENTER /* button2 */
);
else
msg_box(
term, /* terminal */
getml(txt,narez,NULL), /* blocks to free */
TEXT_(T_DELETE_FOLDER), /* title */
AL_CENTER, /* alignment */
TEXT_(T_SURE_DELETE),cast_uchar " ",TEXT_(T_fOLDER),cast_uchar " \"",txt,cast_uchar "\"?",MSG_BOX_END, /* null-terminated text */
(void *)narez, /* data for ld->delete_item */
2, /* # of buttons */
TEXT_(T_YES),delete_ok,B_ENTER, /* button1 */
TEXT_(T_NO),msg_box_null,B_ESC /* button2 */
);
}
else /* item */
msg_box(
term, /* terminal */
getml(txt,narez,NULL), /* blocks to free */
TEXT_(ld->delete_dialog_title), /* title */
AL_CENTER, /* alignment */
TEXT_(T_SURE_DELETE),cast_uchar " ",TEXT_(ld->item_description),cast_uchar " \"",txt,cast_uchar "\"?",MSG_BOX_END, /* null-terminated text */
(void *)narez, /* data for ld->delete_item */
2, /* # of buttons */
TEXT_(T_YES),delete_ok,B_ENTER, /* button1 */
TEXT_(T_NO),msg_box_null,B_ESC /* button2 */
);
return 0;
}
static void redraw_list_element(struct terminal *term, struct dialog_data *dlg, int y, int w, struct list_description *ld, struct list *l)
{
struct list *lx;
unsigned char *xp;
int xd;
unsigned char *txt;
int x=0;
unsigned char color = 0;
long bgcolor = 0, fgcolor = 0;
int b;
unsigned char element;
if (!F) {
color = l == ld->current_pos ? COLOR_MENU_SELECTED : COLOR_MENU_TEXT;
}
#ifdef G
else {
struct rect r;
r.x1 = dlg->x + DIALOG_LB;
r.x2 = dlg->x + DIALOG_LB + w;
r.y1 = y;
r.y2 = y + G_BFU_FONT_SIZE;
if (dlg->s) exclude_rect_from_set(&dlg->s, &r);
if (!do_rects_intersect(&r, &term->dev->clip))
return;
bgcolor = l == ld->current_pos ? bfu_fg_color : bfu_bg_color;
fgcolor = l == ld->current_pos ? bfu_bg_color : bfu_fg_color;
}
#endif
txt = ld->type_item(term, l, 1);
if (!txt) {
txt = stracpy(cast_uchar "");
}
/* everything except head */
if (l != ld->list) {
switch (ld->type) {
case 0:
element = BFU_ELEMENT_TEE;
if (list_next(l) == ld->list)
element = BFU_ELEMENT_L;
x += draw_bfu_element(term, dlg->x + DIALOG_LB, y, color, bgcolor, fgcolor, element, l->type & 4);
break;
case 1:
xp = mem_alloc(l->depth + 1);
memset(xp, 0, l->depth + 1);
xd = l->depth + 1;
for (lx = list_next(l); lx != ld->list; lx = list_next(lx)) {
if (lx->depth < xd) {
xd = lx->depth;
xp[xd] = 1;
if (!xd) break;
}
}
for (b = 0; b < l->depth; b++)
x += draw_bfu_element(term, dlg->x + DIALOG_LB+x, y, color, bgcolor, fgcolor, xp[b] ? BFU_ELEMENT_PIPE : BFU_ELEMENT_EMPTY, 0);
if (l->depth >= 0) { /* everything except head */
int o = xp[l->depth];
switch (l->type & 1) {
case 0: /* item */
element = o ? BFU_ELEMENT_TEE : BFU_ELEMENT_L;
break;
case 1: /* directory */
if (l->type & 2) {
element = o ? BFU_ELEMENT_OPEN_DOWN : BFU_ELEMENT_OPEN;
} else {
element = o ? BFU_ELEMENT_CLOSED_DOWN : BFU_ELEMENT_CLOSED;
}
break;
default: /* this should never happen */
internal_error("=8-Q lunacy level too high! Call "BOHNICE".\n");
element = BFU_ELEMENT_EMPTY;
}
x += draw_bfu_element(term, dlg->x + DIALOG_LB + x, y, color, bgcolor, fgcolor, element, l->type & 4);
}
mem_free(xp);
break;
default:
internal_error(
"Invalid list description type.\n"
"Somebody's probably shooting into memory.\n"
"_______________\n"
"`--|_____|--|___ `\\\n"
" \" \\___\\\n");
}
}
if (!F) {
print_text(term, dlg->x + x + DIALOG_LB, y, w-x, txt, color);
x+=cp_len(term_charset(term), txt);
fill_area(term, dlg->x+DIALOG_LB+x, y, w-x, 1, ' ', 0);
set_line_color(term, dlg->x + DIALOG_LB + x, y, w-x, color);
}
#ifdef G
else {
struct rect old_area;
struct style *stl = l == ld->current_pos ? bfu_style_wb : bfu_style_bw;
restrict_clip_area(term->dev, &old_area, dlg->x + x + DIALOG_LB, y, dlg->x + DIALOG_LB + w, y + G_BFU_FONT_SIZE);
g_print_text(term->dev, dlg->x + x + DIALOG_LB, y, stl, txt, NULL);
x += g_text_width(stl, txt);
drv->fill_area(term->dev, dlg->x + DIALOG_LB + x, y, dlg->x + DIALOG_LB + w, y + G_BFU_FONT_SIZE, bgcolor);
set_clip_area(term->dev, &old_area);
}
#endif
mem_free(txt);
}
/* redraws list */
static void redraw_list(struct terminal *term, void *bla)
{
struct redraw_data* rd = (struct redraw_data *)bla;
struct list_description *ld = rd->ld;
struct dialog_data *dlg = rd->dlg;
int y, a;
struct list *l;
int w = dlg->xw - 2 * DIALOG_LB - (F ? sirka_scrollovadla : 0);
y = dlg->y + DIALOG_TB;
#ifdef G
if (F) {
int total = get_total_items(ld);
int visible = get_visible_items(ld);
int pos = get_scroll_pos(ld);
draw_vscroll_bar(term->dev, dlg->x + DIALOG_LB + w + G_SCROLL_BAR_WIDTH, y, G_BFU_FONT_SIZE * ld->n_items, total, visible, pos);
if (dlg->s) exclude_from_set(&dlg->s, dlg->x + DIALOG_LB + w + G_SCROLL_BAR_WIDTH, y, dlg->x + DIALOG_LB + w + sirka_scrollovadla, y + G_BFU_FONT_SIZE * ld->n_items);
}
#endif
for (a = 0, l = ld->win_offset; a < ld->n_items; ) {
redraw_list_element(term, dlg, y, w, ld, l);
l = next_in_tree(ld, l);
a++;
y += gf_val(1, G_BFU_FONT_SIZE);
if (l == ld->list) break;
}
if (!F) fill_area(term, dlg->x + DIALOG_LB, y, w, ld->n_items-a, ' ', COLOR_MENU_TEXT);
#ifdef G
else {
drv->fill_area(term->dev, dlg->x + DIALOG_LB, y, dlg->x + DIALOG_LB + w, dlg->y + DIALOG_TB + ld->n_items * G_BFU_FONT_SIZE, dip_get_color_sRGB(G_BFU_BG_COLOR));
if (dlg->s) exclude_from_set(&dlg->s, dlg->x + DIALOG_LB, y, dlg->x + DIALOG_LB + w, dlg->y + DIALOG_TB + ld->n_items * G_BFU_FONT_SIZE);
}
#endif
}
/* moves cursor from old position to a new one */
/* direction: -1=old is previous, +1=old is next */
static void redraw_list_line(struct terminal *term, void *bla)
{
struct redraw_data *rd = (struct redraw_data *)bla;
struct list_description *ld = rd->ld;
struct dialog_data *dlg = rd->dlg;
int direction = rd->n;
int w = dlg->xw - 2 * DIALOG_LB - (F ? sirka_scrollovadla : 0);
int y = dlg->y + DIALOG_TB + gf_val(ld->win_pos, ld->win_pos * G_BFU_FONT_SIZE);
struct list *l;
redraw_list_element(term, dlg, y, w, ld, ld->current_pos);
if (!F && (!term->spec->block_cursor || term->spec->braille)) {
set_cursor(term, dlg->x + DIALOG_LB, y, dlg->x + DIALOG_LB, y);
}
y += gf_val(direction, direction*G_BFU_FONT_SIZE);
switch (direction) {
case 0:
l = NULL;
break;
case 1:
l = next_in_tree(ld, ld->current_pos);
break;
case -1:
l = prev_in_tree(ld, ld->current_pos);
break;
default:
internal_error("redraw_list_line: invalid direction %d", direction);
l = NULL;
break;
}
if (l) redraw_list_element(term, dlg, y, w, ld, l);
}
/* like redraw_list, but scrolls window, prints new line to top/bottom */
/* in text mode calls redraw list */
/* direction: -1=up, 1=down */
static void scroll_list(struct terminal *term, void *bla)
{
#ifdef G
struct redraw_data *rd = (struct redraw_data *)bla;
struct list_description *ld = rd->ld;
struct dialog_data *dlg = rd->dlg;
int direction = rd->n;
#endif
if (!F) {
redraw_list(term, bla);
}
#ifdef G
else {
struct rect old_area;
struct graphics_device *dev = term->dev;
int w = dlg->xw - 2 * DIALOG_LB - sirka_scrollovadla;
int y = dlg->y + DIALOG_TB;
int top = 0, bottom = 0;
switch (direction) {
case 1: /* down */
top = G_BFU_FONT_SIZE;
break;
case -1: /* up */
bottom = -G_BFU_FONT_SIZE;
break;
default:
internal_error("Wrong direction %d in function scroll_list.\n",direction);
}
restrict_clip_area(term->dev, &old_area, dlg->x + DIALOG_LB, y + top, dlg->x + DIALOG_LB + w, y + bottom + G_BFU_FONT_SIZE * ld->n_items);
if (drv->flags & GD_DONT_USE_SCROLL && overwrite_instead_of_scroll) {
redraw_list(term, bla);
} else {
int j;
struct rect_set *rs = g_scroll(dev, 0, top + bottom);
for (j = 0; j < rs->m; j++) {
struct rect *r = &rs->r[j];
struct rect clip1;
restrict_clip_area(term->dev, &clip1, r->x1, r->y1, r->x2, r->y2);
redraw_list(term, bla);
set_clip_area(term->dev, &clip1);
}
mem_free(rs);
}
set_clip_area(term->dev, &old_area);
/* redraw scroll bar */
{
int total = get_total_items(ld);
int visible = get_visible_items(ld);
int pos = get_scroll_pos(ld);
draw_vscroll_bar(term->dev, dlg->x + DIALOG_LB + w + G_SCROLL_BAR_WIDTH, y, G_BFU_FONT_SIZE * ld->n_items, total, visible, pos);
if (dlg->s) exclude_from_set(&dlg->s, dlg->x + DIALOG_LB + w + G_SCROLL_BAR_WIDTH, y, dlg->x + DIALOG_LB + w + sirka_scrollovadla, y + G_BFU_FONT_SIZE * ld->n_items);
}
}
#endif
}
static int list_item_mark(struct dialog_data *dlg, struct dialog_item_data *useless)
{
struct list_description *ld = (struct list_description *)dlg->dlg->udata2;
static struct redraw_data rd;
rd.ld = ld;
rd.dlg = dlg;
rd.n = 0;
if (ld->current_pos != ld->list) ld->current_pos->type ^= 4;
if (next_in_tree(ld, ld->current_pos) == ld->list) /* already at the bottom */
{
draw_to_window(dlg->win, redraw_list_line, &rd);
return 0;
}
rd.n = -1;
ld->current_pos = next_in_tree(ld, ld->current_pos);
ld->win_pos++;
if (ld->win_pos > ld->n_items - 1) /* scroll down */
{
ld->win_pos = ld->n_items - 1;
ld->win_offset = next_in_tree(ld, ld->win_offset);
draw_to_window(dlg->win, scroll_list, &rd);
}
draw_to_window(dlg->win, redraw_list_line, &rd);
return 0;
}
static void list_find_next(struct redraw_data *rd, int direction)
{
struct list_description *ld=rd->ld;
struct dialog_data *dlg=rd->dlg;
struct session *ses=(struct session *)(dlg->dlg->udata);
struct list* item;
if (!ld->search_word) {msg_box(ses->term, NULL, TEXT_(T_SEARCH), AL_CENTER, TEXT_(T_NO_PREVIOUS_SEARCH), MSG_BOX_END, NULL, 1, TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC); return;}
if ((item=ld->find_item(ld->current_pos,ld->search_word,direction)))
{
struct list *l;
ld->current_pos=item;
ld->win_offset=item;
ld->win_pos=0;
if (ld->type)
for (l=item;l->depth>=0;l=l->fotr)
if (l!=item) l->type|=2;
draw_to_window(dlg->win,redraw_list,rd);
if (!F) if (!ses->term->spec->block_cursor || ses->term->spec->braille) set_cursor(ses->term, dlg->x + DIALOG_LB, dlg->y+DIALOG_TB+ld->win_pos, dlg->x + DIALOG_LB, dlg->y+DIALOG_TB+ld->win_pos);
}
else
msg_box(ses->term, NULL, TEXT_(T_SEARCH), AL_CENTER, TEXT_(T_SEARCH_STRING_NOT_FOUND), MSG_BOX_END, NULL, 1, TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
}
static void list_search_for_back(void *rd_, unsigned char *str)
{
struct redraw_data *rd = (struct redraw_data *)rd_;
struct list_description *ld = rd->ld;
if (!*str) return;
if (!ld->open) return;
if (ld->search_word) mem_free(ld->search_word);
ld->search_word = to_utf8_upcase(str, term_charset(rd->dlg->win->term));
ld->search_direction = -1;
list_find_next(rd, ld->search_direction);
}
static void list_search_for(void *rd_, unsigned char *str)
{
struct redraw_data *rd = (struct redraw_data *)rd_;
struct list_description *ld = rd->ld;
if (!*str) return;
if (!ld->open) return;
if (ld->search_word) mem_free(ld->search_word);
ld->search_word = to_utf8_upcase(str, term_charset(rd->dlg->win->term));
ld->search_direction = 1;
list_find_next(rd, ld->search_direction);
}
static void list_edit_toggle(struct dialog_data *dlg, struct list_description *ld)
{
static struct redraw_data rd;
ld->current_pos->type^=2;
if (!(ld->current_pos->type&2))unselect_in_folder(ld, ld->current_pos);
rd.ld=ld;
rd.dlg=dlg;
rd.n=0;
draw_to_window(dlg->win,redraw_list,&rd);
draw_to_window(dlg->win,redraw_list_line,&rd); /* set cursor */
}
static int list_event_handler(struct dialog_data *dlg, struct links_event *ev)
{
struct list_description *ld=(struct list_description*)(dlg->dlg->udata2);
static struct redraw_data rd;
struct session *ses=(struct session *)(dlg->dlg->udata);
rd.ld=ld;
rd.dlg=dlg;
rd.n=0;
switch ((int)ev->ev)
{
case EV_KBD:
if (ev->y & KBD_PASTING) break;
if (ld->type==1) /* tree list */
{
if (ev->x==' ') /* toggle folder */
{
if (!((ld->current_pos->type)&1))return EVENT_PROCESSED; /* item */
list_edit_toggle(dlg, ld);
return EVENT_PROCESSED;
}
if (ev->x=='+'||ev->x=='=') /* open folder */
{
if (!((ld->current_pos->type)&1))return EVENT_PROCESSED; /* item */
if ((ld->current_pos->type)&2)return EVENT_PROCESSED; /* already open */
list_edit_toggle(dlg, ld);
return EVENT_PROCESSED;
}
if (ev->x=='-') /* close folder */
{
if (!((ld->current_pos->type)&1))return EVENT_PROCESSED; /* item */
if (!((ld->current_pos->type)&2))return EVENT_PROCESSED; /* already closed */
list_edit_toggle(dlg, ld);
return EVENT_PROCESSED;
}
}
if (ev->x == '/' || (ev->x == KBD_FIND && !(ev->y & (KBD_SHIFT | KBD_CTRL | KBD_ALT)))) /* search forward */
{
struct redraw_data *r;
r=mem_alloc(sizeof(struct redraw_data));
r->ld=ld;
r->dlg=dlg;
input_field(ses->term, getml(r,NULL), TEXT_(T_SEARCH), TEXT_(T_SEARCH_FOR_TEXT), r, ld->search_history, MAX_INPUT_URL_LEN, cast_uchar "", 0, 0, NULL, 2, TEXT_(T_OK), list_search_for, TEXT_(T_CANCEL), input_field_null);
return EVENT_PROCESSED;
}
if (ev->x == '?' || (ev->x == KBD_FIND && ev->y & (KBD_SHIFT | KBD_CTRL | KBD_ALT))) /* search back */
{
struct redraw_data *r;
r=mem_alloc(sizeof(struct redraw_data));
r->ld=ld;
r->dlg=dlg;
input_field(ses->term, getml(r,NULL), TEXT_(T_SEARCH_BACK), TEXT_(T_SEARCH_FOR_TEXT), r, ld->search_history, MAX_INPUT_URL_LEN, cast_uchar "", 0, 0, NULL, 2, TEXT_(T_OK), list_search_for_back, TEXT_(T_CANCEL), input_field_null);
return EVENT_PROCESSED;
}
if ((ev->x == 'n' && !(ev->y & KBD_ALT)) || ev->x == KBD_REDO) /* find next */
{
list_find_next(&rd, ld->search_direction);
return EVENT_PROCESSED;
}
if ((ev->x == 'N' && !(ev->y & KBD_ALT)) || ev->x == KBD_UNDO) /* find prev */
{
list_find_next(&rd, - ld->search_direction);
return EVENT_PROCESSED;
}
if (ev->x == KBD_UP)
{
if (ld->current_pos==ld->list) goto kbd_up_redraw_exit; /* already on the top */
ld->current_pos=prev_in_tree(ld,ld->current_pos);
ld->win_pos--;
rd.n=1;
if (ld->win_pos<0) /* scroll up */
{
ld->win_pos=0;
ld->win_offset=prev_in_tree(ld,ld->win_offset);
draw_to_window(dlg->win,scroll_list,&rd);
}
kbd_up_redraw_exit:
draw_to_window(dlg->win,redraw_list_line,&rd);
return EVENT_PROCESSED;
}
if (ev->x == 'i' || ev->x == '*' || ev->x == '8' || ev->x == KBD_INS || ev->x == KBD_SELECT)
{
list_item_mark(dlg, NULL);
return EVENT_PROCESSED;
}
if (ev->x == KBD_DOWN)
{
if (next_in_tree(ld,ld->current_pos)==ld->list) goto kbd_down_redraw_exit; /* already at the bottom */
ld->current_pos=next_in_tree(ld,ld->current_pos);
ld->win_pos++;
rd.n=-1;
if (ld->win_pos>ld->n_items-1) /* scroll down */
{
ld->win_pos=ld->n_items-1;
ld->win_offset=next_in_tree(ld,ld->win_offset);
draw_to_window(dlg->win,scroll_list,&rd);
}
kbd_down_redraw_exit:
draw_to_window(dlg->win,redraw_list_line,&rd);
return EVENT_PROCESSED;
}
if (ev->x == KBD_HOME || (upcase(ev->x) == 'A' && ev->y & KBD_CTRL))
{
if (ld->current_pos==ld->list) goto kbd_home_redraw_exit; /* already on the top */
ld->win_offset=ld->list;
ld->current_pos=ld->win_offset;
ld->win_pos=0;
rd.n=0;
draw_to_window(dlg->win,redraw_list,&rd);
kbd_home_redraw_exit:
draw_to_window(dlg->win,redraw_list_line,&rd); /* set cursor */
return EVENT_PROCESSED;
}
if (ev->x == KBD_END || (upcase(ev->x) == 'E' && ev->y & KBD_CTRL))
{
int a;
if (ld->current_pos==prev_in_tree(ld,ld->list)) goto kbd_end_redraw_exit; /* already on the top */
ld->win_offset=prev_in_tree(ld,ld->list);
for (a=1;a<ld->n_items&&ld->win_offset!=ld->list;a++)
ld->win_offset=prev_in_tree(ld,ld->win_offset);
ld->current_pos=prev_in_tree(ld,ld->list);
ld->win_pos=a-1;
rd.n=0;
draw_to_window(dlg->win,redraw_list,&rd);
kbd_end_redraw_exit:
draw_to_window(dlg->win,redraw_list_line,&rd); /* set cursor */
return EVENT_PROCESSED;
}
if (ev->x == KBD_PAGE_UP || (upcase(ev->x) == 'B' && ev->y & KBD_CTRL))
{
int a;
if (ld->current_pos==ld->list) goto kbd_page_up_redraw_exit; /* already on the top */
for (a=0;a<ld->n_items&&ld->win_offset!=ld->list;a++)
{
ld->win_offset=prev_in_tree(ld,ld->win_offset);
ld->current_pos=prev_in_tree(ld,ld->current_pos);
}
if (a<ld->n_items){ld->current_pos=ld->win_offset;ld->win_pos=0;}
rd.n=0;
draw_to_window(dlg->win,redraw_list,&rd);
kbd_page_up_redraw_exit:
draw_to_window(dlg->win,redraw_list_line,&rd); /* set cursor */
return EVENT_PROCESSED;
}
if (ev->x == KBD_PAGE_DOWN || (upcase(ev->x) == 'F' && ev->y & KBD_CTRL))
{
int a;
struct list*p=ld->win_offset;
if (ld->current_pos==prev_in_tree(ld,ld->list)) goto kbd_page_down_redraw_exit; /* already on the bottom */
for (a=0;a<ld->n_items&&ld->list!=next_in_tree(ld,p);a++)
p=next_in_tree(ld,p);
if (a<ld->n_items) /* already last screen */
{
ld->current_pos=p;
ld->win_pos=a;
rd.n=0;
draw_to_window(dlg->win,redraw_list,&rd);
draw_to_window(dlg->win,redraw_list_line,&rd); /* set cursor */
return EVENT_PROCESSED;
}
/* here is whole screen only - the window was full before pressing the page-down key */
/* p is pointing behind last item of the window (behind last visible item in the window) */
for (a=0;a<ld->n_items&&p!=ld->list;a++)
{
ld->win_offset=next_in_tree(ld,ld->win_offset);
ld->current_pos=next_in_tree(ld,ld->current_pos);
p=next_in_tree(ld,p);
}
if (a<ld->n_items){ld->current_pos=prev_in_tree(ld,ld->list);ld->win_pos=ld->n_items-1;}
rd.n=0;
draw_to_window(dlg->win,redraw_list,&rd);
kbd_page_down_redraw_exit:
draw_to_window(dlg->win,redraw_list_line,&rd); /* set cursor */
return EVENT_PROCESSED;
}
break;
case EV_MOUSE:
/* toggle select item */
if ((ev->b & BM_ACT) == B_DOWN && (ev->b & BM_BUTT) == B_RIGHT) {
int n,a;
struct list *l=ld->win_offset;
last_mouse_y=ev->y;
if (
(ev->y)<(dlg->y+DIALOG_TB)||
(ev->y)>=(dlg->y+DIALOG_TB+gf_val(ld->n_items,G_BFU_FONT_SIZE*(ld->n_items)))||
(ev->x)<(dlg->x+DIALOG_LB)||
(ev->x)>(dlg->x+dlg->xw-DIALOG_LB-(F?sirka_scrollovadla:0))
)break; /* out of the dialog */
n=(ev->y-dlg->y-DIALOG_TB)/gf_val(1,G_BFU_FONT_SIZE);
for (a=0;a<n;a++)
{
struct list *l1;
l1=next_in_tree(ld,l); /* current item under the mouse pointer */
if (l1==ld->list)goto break2;
else l=l1;
}
/*a=ld->type?((l->depth)>=0?(l->depth)+1:0):(l->depth>=0);*/
l->type^=4;
ld->current_pos=l;
ld->win_pos=n;
rd.n=0;
draw_to_window(dlg->win,redraw_list,&rd);
draw_to_window(dlg->win,redraw_list_line,&rd); /* set cursor */
return EVENT_PROCESSED;
}
/* click on item */
if (((ev->b & BM_ACT) == B_DOWN || (ev->b & BM_ACT) == B_DRAG) && (ev->b & BM_BUTT) == B_LEFT) {
int n,a;
struct list *l=ld->win_offset;
last_mouse_y=ev->y;
if (
(ev->y)<(dlg->y+DIALOG_TB)||
(ev->y)>=(dlg->y+DIALOG_TB+gf_val(ld->n_items,G_BFU_FONT_SIZE*(ld->n_items)))||
(ev->x)<(dlg->x+DIALOG_LB)||
(ev->x)>(dlg->x+dlg->xw-DIALOG_LB-(F?sirka_scrollovadla:0))
)goto skip_item_click; /* out of the dialog */
n=(ev->y-dlg->y-DIALOG_TB)/gf_val(1,G_BFU_FONT_SIZE);
for (a=0;a<n;a++)
{
struct list *l1;
l1=next_in_tree(ld,l); /* current item under the mouse pointer */
if (l1==ld->list) {
n=a;
break;
}
else l=l1;
}
a=ld->type?((l->depth)>=0?(l->depth)+1:0):(l->depth>=0);
ld->current_pos=l;
/* clicked on directory graphical stuff */
if ((ev->b & BM_ACT) == B_DOWN && ld->type && ev->x < (dlg->x + DIALOG_LB + a * BFU_ELEMENT_WIDTH) && (l->type & 1)) {
l->type^=2;
if (!(l->type&2))unselect_in_folder(ld, ld->current_pos);
}
ld->win_pos=n;
rd.n=0;
draw_to_window(dlg->win,redraw_list,&rd);
draw_to_window(dlg->win,redraw_list_line,&rd); /* set cursor */
return EVENT_PROCESSED;
}
/* scroll with the bar */
skip_item_click:
#ifdef G
if (F && (((ev->b & BM_ACT) == B_DRAG || (ev->b & BM_ACT) == B_DOWN || (ev->b & BM_ACT) == B_UP) && (ev->b & BM_BUTT) == B_LEFT)) {
int total=get_total_items(ld);
int scroll_pos;
int redraw_all;
int rep = 0;
long delta;
long h=ld->n_items*G_BFU_FONT_SIZE;
if (
(ev->y)<(dlg->y+DIALOG_TB)||
(ev->y)>=(dlg->y+DIALOG_TB+G_BFU_FONT_SIZE*(ld->n_items))||
(ev->x)<(dlg->x+dlg->xw-DIALOG_LB-G_SCROLL_BAR_WIDTH)||
(ev->x)>(dlg->x+dlg->xw-DIALOG_LB)
)break; /* out of the dialog */
again:
rep++;
if (rep > total) return EVENT_PROCESSED;
scroll_pos=get_scroll_pos(ld);
delta=(ev->y-dlg->y-DIALOG_TB)*total/h-scroll_pos;
last_mouse_y=ev->y;
if (delta>0) /* scroll down */
{
struct list *lll=find_last_in_window(ld);
if (next_in_tree(ld,lll)==ld->list)return EVENT_PROCESSED; /* already at the bottom */
redraw_all = ld->current_pos != lll;
ld->current_pos=next_in_tree(ld,lll);
ld->win_offset=next_in_tree(ld,ld->win_offset);
ld->win_pos=ld->n_items-1;
rd.n=-1;
if (!redraw_all) {
draw_to_window(dlg->win,scroll_list,&rd);
draw_to_window(dlg->win,redraw_list_line,&rd);
} else {
draw_to_window(dlg->win,redraw_list,&rd);
}
goto again;
}
if (delta<0) /* scroll up */
{
if (ld->win_offset==ld->list)return EVENT_PROCESSED; /* already on the top */
redraw_all = ld->current_pos != ld->win_offset;
ld->win_offset=prev_in_tree(ld,ld->win_offset);
ld->current_pos=ld->win_offset;
ld->win_pos=0;
rd.n=+1;
if (!redraw_all) {
draw_to_window(dlg->win,scroll_list,&rd);
draw_to_window(dlg->win,redraw_list_line,&rd);
} else {
draw_to_window(dlg->win,redraw_list,&rd);
}
goto again;
}
return EVENT_PROCESSED;
}
#endif
if ((ev->b & BM_ACT) == B_DRAG && (ev->b & BM_BUTT) == B_MIDDLE) {
long delta=(ev->y-last_mouse_y)/MOUSE_SCROLL_DIVIDER;
last_mouse_y=ev->y;
if (delta>0) /* scroll down */
{
if (next_in_tree(ld,ld->current_pos)==ld->list)return EVENT_PROCESSED; /* already at the bottom */
ld->current_pos=next_in_tree(ld,ld->current_pos);
ld->win_pos++;
rd.n=-1;
if (ld->win_pos>ld->n_items-1) /* scroll down */
{
ld->win_pos=ld->n_items-1;
ld->win_offset=next_in_tree(ld,ld->win_offset);
draw_to_window(dlg->win,scroll_list,&rd);
}
draw_to_window(dlg->win,redraw_list_line,&rd);
}
if (delta<0) /* scroll up */
{
if (ld->current_pos==ld->list)return EVENT_PROCESSED; /* already on the top */
ld->current_pos=prev_in_tree(ld,ld->current_pos);
ld->win_pos--;
rd.n=+1;
if (ld->win_pos<0) /* scroll up */
{
ld->win_pos=0;
ld->win_offset=prev_in_tree(ld,ld->win_offset);
draw_to_window(dlg->win,scroll_list,&rd);
}
draw_to_window(dlg->win,redraw_list_line,&rd);
}
return EVENT_PROCESSED;
}
/* mouse wheel */
if ((ev->b & BM_ACT) == B_MOVE && ((ev->b & BM_BUTT) == B_WHEELUP || (ev->b & BM_BUTT) == B_WHEELDOWN || (ev->b & BM_BUTT) == B_WHEELDOWN1 || (ev->b & BM_BUTT) == B_WHEELUP1)) {
int button=(int)ev->b&BM_BUTT;
last_mouse_y=ev->y;
if (button==B_WHEELDOWN||button==B_WHEELDOWN1) /* scroll down */
{
if (next_in_tree(ld,ld->current_pos)==ld->list)return EVENT_PROCESSED; /* already at the bottom */
ld->current_pos=next_in_tree(ld,ld->current_pos);
ld->win_pos++;
rd.n=-1;
if (ld->win_pos>ld->n_items-1) /* scroll down */
{
ld->win_pos=ld->n_items-1;
ld->win_offset=next_in_tree(ld,ld->win_offset);
draw_to_window(dlg->win,scroll_list,&rd);
}
draw_to_window(dlg->win,redraw_list_line,&rd);
}
if (button==B_WHEELUP||button==B_WHEELUP1) /* scroll up */
{
if (ld->current_pos==ld->list)return EVENT_PROCESSED; /* already on the top */
ld->current_pos=prev_in_tree(ld,ld->current_pos);
ld->win_pos--;
rd.n=+1;
if (ld->win_pos<0) /* scroll up */
{
ld->win_pos=0;
ld->win_offset=prev_in_tree(ld,ld->win_offset);
draw_to_window(dlg->win,scroll_list,&rd);
}
draw_to_window(dlg->win,redraw_list_line,&rd);
}
return EVENT_PROCESSED;
}
break2:
break;
case EV_INIT:
case EV_RESIZE:
case EV_REDRAW:
case EV_ABORT:
break;
default:
break;
}
return EVENT_NOT_PROCESSED;
}
/* display function for the list window */
static void create_list_window_fn(struct dialog_data *dlg)
{
struct terminal *term=dlg->win->term;
struct list_description *ld=(struct list_description*)(dlg->dlg->udata2);
int min=0;
int w,rw,y;
int n_items;
struct redraw_data rd;
int a=7;
ld->dlg=dlg;
if (ld->button_fn)a++; /* user button */
if (ld->type==1)a++; /* add directory button */
y = 0;
min_buttons_width(term, dlg->items, a, &min);
w = term->x * 19 / 20 - 2 * DIALOG_LB;
if (w<min)w=min;
if (w>term->x-2*DIALOG_LB)w=term->x-2*DIALOG_LB;
if (w<5)w=5;
rw=0;
dlg_format_buttons(dlg, NULL, dlg->items, a, 0, &y, w, &rw, AL_CENTER);
n_items = term->y - y;
if (!term->spec->braille)
n_items -= gf_val(2, 3) * DIALOG_TB + gf_val(2, 2*G_BFU_FONT_SIZE);
else
n_items -= 2;
#ifdef G
if (F) n_items /= G_BFU_FONT_SIZE;
#endif
if (n_items < 2) n_items = 2;
ld->n_items = n_items;
while (ld->win_pos > ld->n_items - 1) {
ld->current_pos=prev_in_tree(ld,ld->current_pos);
ld->win_pos--;
}
y += gf_val(ld->n_items,ld->n_items*G_BFU_FONT_SIZE);
rw=w;
dlg->xw=rw+2*DIALOG_LB;
dlg->yw=y+2*DIALOG_TB;
center_dlg(dlg);
draw_dlg(dlg);
rd.ld=ld;
rd.dlg=dlg;
rd.n=0;
draw_to_window(dlg->win,redraw_list,&rd);
y=dlg->y+DIALOG_TB+gf_val(ld->n_items+1,(ld->n_items+1)*G_BFU_FONT_SIZE);
dlg_format_buttons(dlg, term, dlg->items, a, dlg->x+DIALOG_LB, &y, w, &rw, AL_CENTER);
}
static void close_list_window(struct dialog_data *dlg)
{
struct dialog *d=dlg->dlg;
struct list_description *ld=(struct list_description*)(d->udata2);
ld->open=0;
ld->dlg=NULL;
if (ld->search_word) mem_free(ld->search_word);
ld->search_word=NULL;
if (ld->save) ld->save(d->udata);
}
int test_list_window_in_use(struct list_description *ld, struct terminal *term)
{
if (ld->open) {
if (term)
msg_box(
term,
NULL,
TEXT_(T_INFO),
AL_CENTER,
TEXT_(ld->already_in_use),MSG_BOX_END,
NULL,
1,
TEXT_(T_CANCEL),msg_box_null,B_ENTER|B_ESC
);
return 1;
}
return 0;
}
/* dialog->udata2 ... list_description */
/* dialog->udata ... session */
int create_list_window(
struct list_description *ld,
struct list *list,
struct terminal *term,
struct session *ses
)
{
struct dialog *d;
int a;
/* zavodime, zavodime... */
if (test_list_window_in_use(ld, term))
return 1;
ld->open=1;
if (!ld->current_pos)
{
ld->current_pos=list;
ld->win_offset=list;
ld->win_pos=0;
ld->dlg=NULL;
}
a=8;
if (ld->button_fn)a++;
if (ld->type==1)a++;
d = mem_calloc(sizeof(struct dialog) + a * sizeof(struct dialog_item));
d->title=TEXT_(ld->window_title);
d->fn=create_list_window_fn;
d->abort=close_list_window;
d->handle_event=list_event_handler;
d->udata=ses;
d->udata2=ld;
a=0;
if (ld->button_fn)
{
d->items[a].type=D_BUTTON;
d->items[a].fn=list_item_button;
d->items[a].text=TEXT_(ld->button);
a++;
}
if (ld->type==1)
{
d->items[a].type=D_BUTTON;
d->items[a].text=TEXT_(T_FOLDER);
d->items[a].fn=list_folder_add;
a++;
}
d->items[a].type=D_BUTTON;
d->items[a].text=TEXT_(T_ADD);
d->items[a].fn=list_item_add;
d->items[a+1].type=D_BUTTON;
d->items[a+1].text=TEXT_(T_DELETE);
d->items[a+1].fn=list_item_delete;
d->items[a+2].type=D_BUTTON;
d->items[a+2].text=TEXT_(T_EDIT);
d->items[a+2].fn=list_item_edit;
d->items[a+3].type=D_BUTTON;
d->items[a+3].text=TEXT_(T_SELECT);
d->items[a+3].fn=list_item_mark;
d->items[a+4].type=D_BUTTON;
d->items[a+4].text=TEXT_(T_MOVE);
d->items[a+4].fn=list_item_move;
d->items[a+5].type=D_BUTTON;
d->items[a+5].text=TEXT_(T_UNSELECT_ALL);
d->items[a+5].fn=list_item_unselect;
d->items[a+6].type=D_BUTTON;
d->items[a+6].gid=B_ESC;
d->items[a+6].fn=cancel_dialog;
d->items[a+6].text=TEXT_(T_CLOSE);
d->items[a+7].type=D_END;
do_dialog(term, d, getml(d, NULL));
return 0;
}
void reinit_list_window(struct list_description *ld)
{
ld->current_pos=ld->list;
ld->win_offset=ld->list;
ld->win_pos=0;
if (ld->open) internal_error("reinit_list_window: calling reinit while open");
}