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

Last change on this file since 521 was 518, checked in by Sam Hocevar, 12 years ago

imlib: refactor dirty_rect clipping coordiantes so that the upper
bound is no longer inclusive. It will make things easier in the future.

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