/* 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;an_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;an_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 (an_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;an_items&&ld->list!=next_in_tree(ld,p);a++) p=next_in_tree(ld,p); if (an_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;an_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 (an_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;alist)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;alist) { 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 (wterm->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"); }