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

Last change on this file was 682, checked in by Sam Hocevar, 12 years ago

core: rename vec2i to ivec2 and update matrix.h from Lol Engine.

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