source: abuse/trunk/src/imlib/scroller.cpp @ 120

Last change on this file since 120 was 120, checked in by Sam Hocevar, 12 years ago
File size: 14.8 KB
RevLine 
[56]1/*
2 *  Abuse - dark 2D side-scrolling platform game
3 *  Copyright (c) 1995 Crack dot Com
4 *
5 *  This software was released into the Public Domain. As with most public
6 *  domain software, no warranty is made or implied by Crack dot Com or
7 *  Jonathan Clark.
8 */
9
10#include "config.h"
11
[2]12#include "scroller.hpp"
13#define HS_ICON_W 10
14#define HS_ICON_H 8
15
16
[17]17uint8_t hs_left_arrow[10*8]={
[2]18    0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
19    0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
20    1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
21    1, 1, 1, 1, 2, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2,
22    0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
23    2, 0, 0, 0, 0};
24
25
[17]26uint8_t hs_right_arrow[10*8]={
[2]27    0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
28    0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
29    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1,
30    1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 0,
31    0, 0, 0, 0, 0, 2, 1, 1, 0, 0, 0, 0, 0, 0, 2,
32    1, 1, 0, 0, 0};
33
34
[17]35uint8_t vs_up_arrow[8*10]={
[2]36    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0,
37    0, 0, 0, 1, 1, 1, 1, 2, 0, 0, 1, 2, 1, 1, 2,
38    1, 2, 0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 1, 1,
39    2, 0, 0, 0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 1,
40    1, 2, 0, 0, 0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0,
41    0, 2, 2, 0, 0};
42
43
[17]44uint8_t vs_down_arrow[8*10]={
[2]45    0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 1, 1, 2, 0,
46    0, 0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 1, 1, 2,
47    0, 0, 0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 1, 1,
48    2, 0, 0, 0, 1, 2, 1, 1, 2, 1, 2, 0, 0, 1, 1,
49    1, 1, 2, 0, 0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0,
50    0, 0, 0, 0, 0};
51
[106]52void show_icon(image *screen, int x, int y, int icw, int ich, uint8_t *buf)
[2]53{
54  short cx1,cy1,cx2,cy2;
55  screen->get_clip(cx1,cy1,cx2,cy2);
[17]56  uint8_t remap[3];
[2]57  remap[0]=wm->medium_color();
58  remap[1]=wm->bright_color();
59  remap[2]=wm->dark_color();
60
[115]61  screen->lock();
[2]62  for (int yc=ich;yc;yc--,y++)
63  {
64    if (y>=cy1 && y<=cy2)
65    {
[17]66      uint8_t *sl=screen->scan_line(y)+x;
[2]67      for (int xc=icw,xo=x;xc;xc--,xo++,sl++,buf++)
68      {
69        if (xo>=cx1 && xo<=cx2)
70          *sl=remap[*buf];
71      }
72    }
73  }
74  screen->add_dirty(x,y,x+icw-1,y+ich-1);
[115]75  screen->unlock();
[2]76}
77
78scroller::scroller(int X, int Y, int ID, int L, int H, int Vert, int Total_items, ifield *Next)
79{  x=X; y=Y; id=ID; next=Next;  l=L; h=H;  sx=0;  t=Total_items;  drag=-1; vert=Vert;
80}
81
82
[106]83void scroller::area(int &x1, int &y1, int &x2, int &y2)
[2]84
[106]85  area_config();
[2]86  x1=x-1; y1=y-1; 
87  if (vert)
88  { x2=x+l+bw();  y2=y+h; }
89  else
90  { x2=x+l;  y2=y+h+bh(); }
91}
92
93void scroller::dragger_area(int &x1, int &y1, int &x2, int &y2)
94
95  if (vert)
96  { x1=x+l; y1=y+bh(); x2=x+l+bw()-1; y2=y+h-bh()-1; }
97  else { x1=x+bw(); y1=y+h; x2=x+l-bw(); y2=y+h+bh()-1; }
98}
99
100int scroller::bh() { if (vert) return 15; else return 13; }
101int scroller::bw() { if (vert) return 12; else return 14; }
102
[17]103uint8_t *scroller::b1()
[2]104{
105  if (vert) return vs_up_arrow;
106  else return hs_left_arrow;
107}
108
[17]109uint8_t *scroller::b2()
[2]110{
111  if (vert) return vs_down_arrow;
112  else return hs_right_arrow;
113}
114
115
[106]116void scroller::draw_first(image *screen)
[2]117{
118  if (sx>=t) sx=t-1;
[106]119  draw(0,screen);
[112]120  screen->widget_bar(b1x(),b1y(),b1x()+bw()-1,b1y()+bh()-1,
[2]121                    wm->bright_color(),wm->medium_color(),wm->dark_color());
[112]122  screen->widget_bar(b2x(),b2y(),b2x()+bw()-1,b2y()+bh()-1,
[2]123                    wm->bright_color(),wm->medium_color(),wm->dark_color());
[106]124  show_icon(screen,b1x()+2,b1y()+2,bw()-4,bh()-4,b1());
125  show_icon(screen,b2x()+2,b2y()+2,bw()-4,bh()-4,b2());
[2]126
127  int x1,y1,x2,y2;
128  dragger_area(x1,y1,x2,y2);
129  screen->bar(x1,y1,x2,y2,wm->black());
130  screen->bar(x1+1,y1+1,x2-1,y2-1,wm->medium_color());   
[112]131  draw_widget(screen,0);
[106]132  scroll_event(sx,screen);
[2]133}
134
135void scroller::wig_area(int &x1, int &y1, int &x2, int &y2)
136{
137  int sx1,sy1,sx2,sy2;
138  dragger_area(sx1,sy1,sx2,sy2);
139  if (vert)
140  {
141    x1=x+l+1;
142    if (t<2)
143      y1=y+bh()+1;
144    else
145      y1=y+bh()+1+sx*(sy2-sy1+1-bh())/(t-1);
146  } else
147  {
148    if (t<2) x1=x+bw()+1;
149    else x1=x+bw()+1+sx*(sx2-sx1+1-bw())/(t-1);
150    y1=y+h+1;
151  }
152  x2=x1+bw()-3;
153  y2=y1+bh()-3; 
154 
155}
156
157
[112]158void scroller::draw_widget(image *screen, int erase)
[2]159{
160  int x1,y1,x2,y2;
161  wig_area(x1,y1,x2,y2);
162  if (erase)
163    screen->bar(x1,y1,x2,y2,wm->medium_color());
164  else
[112]165    screen->widget_bar(x1,y1,x2,y2,
[2]166                      wm->bright_color(),wm->medium_color(),wm->dark_color());
167}
168
[106]169void scroller::draw(int active, image *screen)
[2]170{
171  int x1,y1,x2,y2;
[106]172  area(x1,y1,x2,y2);
[2]173  screen->rectangle(x1,y1,x2,y2,active ? wm->bright_color() : wm->dark_color());
174}
175
176
[120]177void scroller::handle_event(event &ev, image *screen, InputManager *inm)
[2]178{
179  int mx=ev.mouse_move.x,my=ev.mouse_move.y;
180  switch (ev.type)
181  {
182    case EV_MOUSE_BUTTON :
183    {
184      if (ev.mouse_button && drag==-1)
185      {
186        if (mx>=b1x() && mx<b1x()+bw() && my>=b1y()-2 && my<b1y()+bh())
187        {
188          if (sx>0)
189          {
[112]190            draw_widget(screen,1);
[2]191            sx--;
[112]192            draw_widget(screen,0);
[106]193            scroll_event(sx,screen);
[2]194          }
195        } else if (mx>=b2x() && mx<b2x()+bw() && my>=b2y() && my<=b2y()+bh())
196        {
197          if (sx<t-1)
198          {
[112]199            draw_widget(screen,1);
[2]200            sx++;
[112]201            draw_widget(screen,0);
[106]202            scroll_event(sx,screen);
[2]203          }         
204        }
205        else
206        {
207          int dx1,dy1,dx2,dy2;
208          dragger_area(dx1,dy1,dx2,dy2);
209          if (mx>=dx1 && mx<=dx2 && my>=dy1 && my<=dy2)
210          {
211            int x1,y1,x2,y2;
212            wig_area(x1,y1,x2,y2);
213            if (mx>=x1 && mx<=x2 && my>=y1 && my<=y2)
214            {
215              drag=sx;
216              inm->grab_focus(this);
217            }         
218            else if (t>1)
219            {
220              int nx=mouse_to_drag(mx,my);
221              if (nx!=sx && nx>=0 && nx<t)
222              {
[112]223                draw_widget(screen,1);
[2]224                sx=nx;
[112]225                draw_widget(screen,0);
[106]226                scroll_event(sx,screen);               
[2]227              }         
228            }
[106]229          } else handle_inside_event(ev,screen,inm);
[2]230        }
231      } else if (!ev.mouse_button && drag!=-1)
232      {
233        inm->release_focus();
234        drag=-1;
235      }
236    } break;
237
238    case EV_MOUSE_MOVE :
239    {
240      if (drag!=-1)
241      {
242        int nx=mouse_to_drag(mx,my);
243        if (nx<0) nx=0; else if (nx>=t) nx=t-1;
244        if (nx!=sx)
245        {
[112]246          draw_widget(screen,1);
[2]247          sx=nx;
[112]248          draw_widget(screen,0);
[106]249          scroll_event(sx,screen);
[2]250        }
251      } else if ( activate_on_mouse_move())
252      {
253        int x1,y1,x2,y2;
254        wig_area(x1,y1,x2,y2);
255        if (mx>=x && mx<=x+l-1 && my>=y && my<=y+h-1)
[106]256          handle_inside_event(ev,screen,inm);
[2]257      }
258
259    } break;
260    case EV_KEY :
261    {
262      switch (ev.key)
263      {
264        case JK_LEFT :
[106]265        { handle_left(screen,inm); } break;
[2]266        case JK_RIGHT :
[106]267        { handle_right(screen,inm); } break;
[2]268        case JK_UP :
[106]269        { handle_up(screen,inm); } break;
[2]270        case JK_DOWN :
[106]271        { handle_down(screen,inm); } break;
[2]272
273        default :
[106]274          handle_inside_event(ev,screen,inm);
[2]275      }
276    } break;
277  }
278}
279
280
[120]281void scroller::handle_right(image *screen, InputManager *inm)
[2]282{
283  if (!vert && sx<t-1)
284  {
[112]285    draw_widget(screen,1);
[2]286    sx++;
[112]287    draw_widget(screen,0);
[106]288    scroll_event(sx,screen);       
[2]289  }
290}
291
[120]292void scroller::handle_left(image *screen, InputManager *inm)
[2]293{
294  if (!vert && sx>1)
295  {
[112]296    draw_widget(screen,1);
[2]297    sx--;
[112]298    draw_widget(screen,0);
[106]299    scroll_event(sx,screen);       
[2]300  }
301}
302
[120]303void scroller::handle_up(image *screen, InputManager *inm)
[2]304{
305  if (vert && sx>1)
306  {
[112]307    draw_widget(screen,1);
[2]308    sx--;
[112]309    draw_widget(screen,0);
[106]310    scroll_event(sx,screen);       
[2]311  }
312}
313
[120]314void scroller::handle_down(image *screen, InputManager *inm)
[2]315{
316  if (vert && sx<t-1)
317  {
[112]318    draw_widget(screen,1);
[2]319    sx++;
[112]320    draw_widget(screen,0);
[106]321    scroll_event(sx,screen);       
[2]322  }
323}
324
[106]325void scroller::set_x (int x, image *screen)
[2]326{
327  if (x<0) x=0;
328  if (x>=t) x=t-1;
329  if (x!=sx)
330  {
[112]331    draw_widget(screen,1);
[2]332    sx=x;
[112]333    draw_widget(screen,0);
[106]334    scroll_event(sx,screen);       
[2]335  }
336}
337
338int scroller::mouse_to_drag(int mx,int my)
339{
340  int x1,y1,x2,y2;
341  dragger_area(x1,y1,x2,y2);
342 
343  if (vert)
344  {
345    int h=(y2-y1+1-bh());
346    if (h)
347      return (my-y-bh()-bh()/2)*(t-1)/h;
348    else return 0;
349  }
350  else
351  {
352    int w=(x2-x1+1-bw());
353    if (w)
354      return (mx-x-bw()-bw()/2)*(t-1)/w;
355    else return 0;
356  }
357}
358
359
[106]360void scroller::scroll_event(int newx, image *screen)
[2]361{
362  screen->bar(x,y,x+l-1,y+h-1,wm->black());
[4]363  int xa,ya,xo=0,yo;
[2]364  if (vert) { xa=0; ya=30; yo=x+5; yo=y+5; } else { xa=30; ya=0; xo=x+5; yo=y+5; }
365  for (int i=newx,c=0;c<30 && i<100;i++,c++)
366  {
367    char st[10];
368    sprintf(st,"%d",i);
369    wm->font()->put_string(screen,xo,yo,st,wm->bright_color());
370    xo+=xa; yo+=ya;
371  }
372}
373
[106]374void pick_list::area_config()
[2]375{
376  l=wid*wm->font()->width();
377  h=th*(wm->font()->height()+1); 
378}
379
380int lis_sort(void const *a, void const *b)
381{
382  pick_list_item *a1=(pick_list_item *)a;
383  pick_list_item *a2=(pick_list_item *)b;
384  return strcmp(a1->name,a2->name);
385}
386
387pick_list::pick_list(int X, int Y, int ID, int height,
388            char **List, int num_entries, int start_yoffset, ifield *Next, image *texture)
389     : scroller(X,Y,ID,2,2,1,0,Next)
390{
391  th=height;
392  tex=texture;
393  t=num_entries;
394  wid=0;
395  key_hist_total=0;
396  lis=(pick_list_item *)jmalloc(sizeof(pick_list_item)*num_entries,"pick list");
397  int i=0;
398  for (;i<num_entries;i++)
399  {
400    lis[i].name=List[i];
401    lis[i].number=i;
402  }
403  qsort((void *)lis,num_entries,sizeof(pick_list_item),lis_sort);
404
405  for (i=0;i<t;i++)
[4]406    if ((int)strlen(List[i])>wid)
[2]407      wid=strlen(List[i]);
408  cur_sel=sx=start_yoffset;
409}
410
[120]411void pick_list::handle_inside_event(event &ev, image *screen, InputManager *inm)
[2]412{
413  if (ev.type==EV_MOUSE_MOVE && activate_on_mouse_move())
414  {
415    int sel=last_sel+(ev.mouse_move.y-y)/(wm->font()->height()+1);
416    if (sel!=cur_sel && sel<t && sel>=0)
417    {
418      cur_sel=sel;
[106]419      scroll_event(last_sel,screen);
[2]420    }   
421  }
422  else if (ev.type==EV_MOUSE_BUTTON)
423  { 
424    int sel=last_sel+(ev.mouse_move.y-y)/(wm->font()->height()+1);
425    if (sel<t && sel>=0)
426    {
427      if (sel==cur_sel)
428      wm->push_event(new event(id,(char *)this));
429      else
430      {
431        cur_sel=sel;
[106]432        scroll_event(last_sel,screen);
[2]433      }     
434    }
435  } else if (ev.type==EV_KEY && ev.key==JK_ENTER) 
436    wm->push_event(new event(id,(char *)this));
437  else if (ev.type==EV_KEY)
438  {
439    int found=-1;
440    if (key_hist_total<20)
[4]441      key_hist[(int)(key_hist_total++)]=ev.key;
[2]442
443    for (int i=0;i<t && found==-1;i++)
444    {
[4]445      if ((int)strlen(lis[i].name)>=key_hist_total && memcmp(lis[i].name,key_hist,key_hist_total)==0)
[2]446        found=i;
447    }
448    if (found!=-1)
449    {
450      sx=found;
451      cur_sel=found;
[106]452      scroll_event(sx,screen);
[2]453    } else key_hist_total=0;
454  }
455}
456
[120]457void pick_list::handle_up(image *screen, InputManager *inm)
[2]458{
459  if (cur_sel>0)
460    cur_sel--;
461  else return ;
462  if (cur_sel<sx)
463  {
[112]464    draw_widget(screen,1);
[2]465    sx=cur_sel;
[112]466    draw_widget(screen,0);
[2]467  }
[106]468  scroll_event(sx,screen);               
[2]469}
470
[120]471void pick_list::handle_down(image *screen, InputManager *inm)
[2]472{
473  if (cur_sel<t-1)
474    cur_sel++;
475  else return ;
476  if (cur_sel>sx+th-1)
477  {
[112]478    draw_widget(screen,1);
[2]479    sx=cur_sel-th+1;
[112]480    draw_widget(screen,0);
[2]481  }
[106]482  scroll_event(sx,screen);               
[2]483}
484
[106]485void pick_list::scroll_event(int newx, image *screen)
[2]486{
487  last_sel=newx;
488  if (tex)
489  {
490    short cx1,cy1,cx2,cy2;
491    screen->get_clip(cx1,cy1,cx2,cy2);
492    screen->set_clip(x,y,x+l-1,y+h-1);
493    int tw=(l+tex->width()-1)/tex->width();
494    int th=(h+tex->height()-1)/tex->height();
[4]495    int dy=y;
[2]496    for (int j=0;j<th;j++,dy+=tex->height())
497      for (int i=0,dx=x;i<tw;i++,dx+=tex->width())     
498        tex->put_image(screen,dx,dy);
499
500    screen->set_clip(cx1,cy1,cx2,cy2);
501  } else screen->bar(x,y,x+l-1,y+h-1,wm->black());
502
503  int dy=y;
504  for (int i=0;i<th;i++,dy+=wm->font()->height()+1)
505  {
506    if (i+newx==cur_sel)
507      screen->bar(x,dy,x+wid*wm->font()->width()-1,dy+wm->font()->height(),wm->dark_color());
508    if (i+newx<t)
509      wm->font()->put_string(screen,x,dy,lis[i+newx].name,wm->bright_color());
510  }
511}
512
513
514
515
516spicker::spicker(int X, int Y, int ID, int Rows, int Cols, int Vert, int MultiSelect,
517               ifield *Next)
518     : scroller(X,Y,ID,2,2,Vert,0,Next)
519{
520  l=-1;
521  last_click=-1;
522  r=Rows;
523  c=Cols;
524  m=MultiSelect;
525  select=NULL;
526}
527
528void spicker::set_select(int x, int on)
529{
530  if (m)
531  {
532    if (on)
533      select[x/8]|=1<<(x&7);
534    else
535      select[x/8]&=(0xff-(1<<(x&7)));
536  } else cur_sel=x; 
537}
538
539int spicker::get_select(int x)
540{
541  if (m)
542    return select[x/8]&(1<<(x&7));
543  else return (x==cur_sel);
544}
545
546int spicker::first_selected()
547{
548  if (m)
549  {
550    for (int i=0;i<t;i++)
551      if (get_select(i)) return i;
552    return -1;
553  } else return cur_sel;
554}
555
556void spicker::reconfigure()
557{
558  if (select)
559    jfree(select);
560  select=NULL;
561
562
563  t=total();
564  if (sx>t)
565    sx=t-1;
566  if (m)
567  {
[17]568    select=(uint8_t *)jmalloc((t+7)/8,"selection bit array");
[2]569    memset(select,0,(t+7)/8);
570  } else cur_sel=0;
571}
572
[106]573void spicker::draw_background(image *screen)
[2]574{
575  screen->bar(x,y,x+l-1,y+h-1,wm->dark_color());
576}
577
578
[106]579void spicker::area_config()
[2]580{
581  if (vert)
[106]582    l=item_width()+4;
[2]583  else
[106]584    l=item_width()*c+4;
[2]585
586  if (vert)
[106]587    h=item_height()*r+4;
[2]588  else
[106]589    h=item_height()+4;
[2]590
591}
592
[106]593void spicker::set_x(int x, image *screen)
[2]594{
595  cur_sel=x;
596  sx=x;
[106]597  scroll_event(x,screen);
[2]598}
599
600
[106]601void spicker::scroll_event(int newx, image *screen)
[2]602{
603  last_sel=newx;
604  int xa,ya,xo,yo;
605  xo=x+2;
606  yo=y+2;
[106]607  if (vert) { xa=0; ya=item_height(); }
608  else { xa=item_width(); ya=0; }
609  draw_background(screen);
[2]610
611  for (int i=newx;i<newx+vis();i++)
612  {
613    if (i<t)
614    {
615      if (m)     
[106]616        draw_item(screen,xo,yo,i,get_select(i));
[2]617      else
[106]618        draw_item(screen,xo,yo,i,i==cur_sel);
[2]619    }
620    xo+=xa; yo+=ya;
621  }
622}
623
624
[120]625void spicker::handle_inside_event(event &ev, image *screen, InputManager *inm)
[2]626{
627  switch (ev.type)
628  {
629    case EV_MOUSE_MOVE :
630    {
631      if (activate_on_mouse_move())
632      {
633        int me;     
634        if (vert)
[106]635          me=last_sel+(ev.mouse_move.y-y)/item_height();
[2]636        else
[106]637          me=last_sel+(ev.mouse_move.x-x)/item_width();
[2]638        if (me<t && me>=0)
639        {
640          if (cur_sel!=me)
641          {
642            cur_sel=me;
[106]643            scroll_event(last_sel,screen);
644            note_new_current(screen,inm,me);
[2]645          }
646        }
647      }
648    } break;
649    case EV_MOUSE_BUTTON :
650    {
651      int me;     
652      if (vert)
[106]653        me=last_sel+(ev.mouse_move.y-y)/item_height();
[2]654      else
[106]655        me=last_sel+(ev.mouse_move.x-x)/item_width();
[2]656      if (me<t && me>=0)
657      {
658        if (m)
659        {         
660          if (ev.mouse_button)
661          {
662            if (ok_to_select(me))
663            {
664              set_select(me,!get_select(me));
[106]665              scroll_event(last_sel,screen);
[2]666              inm->grab_focus(this);
667            }
668          } else last_click=-1;
669
670        } else if (ok_to_select(me))
671        {
672          if (cur_sel==me)         
[106]673            note_selection(screen,inm,me);
[2]674          else
675          {
676            cur_sel=me;
[106]677            scroll_event(last_sel,screen);
678            note_new_current(screen,inm,me);
[2]679          }
680        }
681      }
682    } break;
683  }
684}
685
686
687
[120]688void spicker::handle_up(image *screen, InputManager *inm)
[2]689{
690  if (vert && cur_sel>0)
691  {
692    cur_sel--;
693
694    if (cur_sel<sx)
695    {
[112]696      draw_widget(screen,1);
[2]697      last_sel=sx=cur_sel;
[112]698      draw_widget(screen,0);
[2]699    }
[106]700    scroll_event(last_sel,screen);
701    note_new_current(screen,inm,cur_sel);   
[2]702  }
703}
704
[120]705void spicker::handle_down(image *screen, InputManager *inm)
[2]706{
707  if (vert && cur_sel<t-1)
708    cur_sel++;
709  else return ;
710  if (cur_sel>sx+r-1)
711  {
[112]712    draw_widget(screen,1);
[2]713    last_sel=sx=cur_sel-r+1;
[112]714    draw_widget(screen,0);
[2]715  }
[106]716  scroll_event(sx,screen);
717  note_new_current(screen,inm,cur_sel);   
[2]718}
719
[120]720void spicker::handle_left(image *screen, InputManager *inm)
[2]721{
722}
723
[120]724void spicker::handle_right(image *screen, InputManager *inm)
[2]725{
726}
727
728
729
730
Note: See TracBrowser for help on using the repository browser.