source: abuse/branches/lol/src/imlib/window.cpp @ 732

Last change on this file since 732 was 732, checked in by Sam Hocevar, 8 years ago

build: SDL2 compilation fixes.

File size: 19.0 KB
Line 
1/*
2 *  Abuse - dark 2D side-scrolling platform game
3 *  Copyright (c) 1995 Crack dot Com
4 *  Copyright (c) 2005-2013 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 HAVE_CONFIG_H
12#   include "config.h"
13#endif
14
15#include <string.h>
16
17#include "common.h"
18
19#include "imlib/video.h"
20#include "imlib/image.h"
21#include "imlib/input.h"
22#include "imlib/event.h"
23#include "imlib/filter.h"
24#include "imlib/window.h"
25
26static int jw_left = 3, jw_right = 3, jw_top = 2, jw_bottom = 3;
27
28int frame_top() { return jw_top; }
29int frame_bottom() { return jw_bottom; }
30int frame_left() { return jw_left; }
31int frame_right() { return jw_right; }
32
33//
34//  Sets the size of the border around each window
35//
36void set_frame_size(int x)
37{
38    if(x < 1)
39        x = 1;
40    jw_left = x;
41    jw_right = x;
42    jw_top = 2;
43    jw_bottom = x;
44}
45
46void WindowManager::grab_focus(AWindow *j)
47{
48    m_grab = j;
49}
50
51void WindowManager::release_focus()
52{
53    m_grab = nullptr;
54}
55
56void WindowManager::close_window(AWindow *j)
57{
58    delete j;
59}
60
61void WindowManager::hide_windows()
62{
63    for (AWindow *p = m_first; p; p = p->next)
64    {
65        if (!p->is_hidden())
66        {
67            p->hide();
68            m_surf->AddDirty(p->m_pos, p->m_pos + p->m_size);
69        }
70    }
71}
72
73void WindowManager::show_windows()
74{
75    for (AWindow *p = m_first; p; p = p->next)
76        if (p->is_hidden())
77            show_window(p);
78}
79
80void WindowManager::hide_window(AWindow *j)
81{
82    if (j == m_first)
83        m_first = m_first->next;
84    else
85    {
86        AWindow *k;
87        for (k = m_first; k->next != j; k = k->next)
88            k->m_surf->AddDirty(j->m_pos - k->m_pos,
89                                j->m_pos - k->m_pos + j->m_size);
90        k->m_surf->AddDirty(j->m_pos - k->m_pos,
91                            j->m_pos - k->m_pos + j->m_size);
92        k->next = j->next;
93    }
94    m_surf->AddDirty(j->m_pos, j->m_pos + j->m_size);
95    j->hide();
96}
97
98void WindowManager::show_window(AWindow *j)
99{
100    if (j->is_hidden())
101    {
102        j->show();
103        j->m_surf->AddDirty(ivec2::zero, j->m_size);
104    }
105}
106
107void WindowManager::get_event(Event &ev)
108{
109    Get(ev);
110
111    if (ev.type == EV_KEY)
112        key_state[ev.key] = 1;
113    else if (ev.type == EV_KEYRELEASE)
114        key_state[ev.key] = 0;
115
116    if (state == inputing)
117    {
118        AWindow *j;
119        for (ev.window = NULL, j = m_first; j; j = j->next)
120            if (!j->is_hidden() && ev.mouse_move >= j->m_pos
121                                && ev.mouse_move < j->m_pos + j->m_size)
122                ev.window = j;
123
124        if (!ev.window && m_grab)
125            ev.window = m_grab;
126
127        if (ev.window)
128        {
129            int closew = 0, movew = 0;
130
131            if ((ev.type == EV_MOUSE_BUTTON && ev.mouse_button == 1 && ev.window &&
132                ev.mouse_move >= ev.window->m_pos &&
133                ev.mouse_move < ev.window->m_pos + ivec2(ev.window->m_size.x, ev.window->y1())))
134            {
135                if (ev.mouse_move.x - ev.window->m_pos.x < 11)
136                    closew = 1;
137                else if (ev.window->is_moveable())
138                    movew = 1;
139            }
140            else if (m_grab)
141                ev.window = m_grab;
142
143            if (ev.type == EV_KEY && ev.key == JK_ESC)
144                closew = 1;
145
146            if (closew)
147                ev.type = EV_CLOSE_WINDOW;
148            else if (movew)
149            {
150                int red = 0;
151                if (ev.window == m_first)       // see if we need to raise the window
152                {
153                    m_first = m_first->next;
154                    if (m_first)
155                        red = 1;
156                }
157                else
158                {
159                    AWindow *last = m_first;
160                    for (; last->next != ev.window; last = last->next)
161                        ;
162                    if (ev.window->next)
163                        red = 1;
164                    last->next = ev.window->next;
165                }
166                if (!m_first)
167                    m_first = ev.window;
168                else
169                {
170                    AWindow *last = m_first;
171                    for (; last->next; last=last->next)
172                        ;
173                    last->next = ev.window;
174                }
175                ev.window->next = nullptr;
176                if (red)
177                {
178                    AWindow *j = ev.window;
179/*      m_surf->AddDirty(j->x,j->y,j->x+j->l,j->y+j->h);
180      for (p=m_first; p!=j; p=p->next)
181        p->m_surf->AddDirty(j->x-p->x,j->y-p->y,j->x+j->l-p->x,j->y+j->h-p->y); */
182                    j->m_surf->AddDirty(ivec2::zero, j->m_size);
183                    flush_screen();
184                }
185
186                state = dragging;
187                drag_window = ev.window;
188                drag_mousex = ev.window->m_pos.x - ev.mouse_move.x;
189                drag_mousey = ev.window->m_pos.y - ev.mouse_move.y;
190                ev.type = EV_SPURIOUS;
191            }
192            else if (ev.window)
193                ev.window->inm->handle_event(ev, ev.window);
194        }
195    }
196    else if (state == dragging)
197    {
198        ev.window = drag_window;
199        if (ev.type == EV_MOUSE_BUTTON && ev.mouse_button == 0)  // user released the mouse
200        {
201          state = inputing;
202          ev.type = EV_SPURIOUS;
203        }
204        else if (ev.type == EV_MOUSE_MOVE)
205        {
206           move_window(drag_window,
207                       ev.mouse_move.x + drag_mousex,
208                       ev.mouse_move.y + drag_mousey);
209           flush_screen();
210           ev.type = EV_DRAG_WINDOW;
211           ev.window_position.x = ev.mouse_move.x + drag_mousex;
212           ev.window_position.y = ev.mouse_move.y + drag_mousey;
213        }
214    }
215}
216
217void AWindow::Resize(ivec2 size)
218{
219    m_surf->SetSize(size);
220    m_size = size;
221}
222
223void WindowManager::resize_window(AWindow *j, int l, int h)
224{
225    m_surf->AddDirty(j->m_pos, j->m_pos + j->m_size);
226    for (AWindow *p = m_first; p != j; p = p->next)
227        p->m_surf->AddDirty(j->m_pos - p->m_pos,
228                            j->m_pos - p->m_pos + j->m_size);
229    j->Resize(ivec2(l, h));
230    if (!frame_suppress)
231        j->redraw();
232}
233
234void WindowManager::move_window(AWindow *j, int x, int y)
235{
236    m_surf->AddDirty(j->m_pos, j->m_pos + j->m_size);
237    for(AWindow *p = m_first; p != j; p = p->next)
238        p->m_surf->AddDirty(j->m_pos - p->m_pos,
239                            j->m_pos - p->m_pos + j->m_size);
240    j->m_pos.x = x;
241    j->m_pos.y = y;
242    j->m_surf->AddDirty(ivec2::zero, j->m_size);
243}
244
245WindowManager::WindowManager(AImage *screen, Palette *pal, int Hi,
246                             int Med, int Low, JCFont *Font)
247  : EventHandler(screen, pal)
248{
249    wm = this;
250    m_surf = screen;
251    hi = Hi; low = Low; med = Med; m_first = NULL; m_pal = pal; m_grab = NULL;
252    bk = pal->FindClosest(u8vec3::zero);
253    state = inputing; fnt = Font;  wframe_fnt = Font;
254    memset(key_state, 0, sizeof(key_state));
255    frame_suppress = 0;
256}
257
258WindowManager::~WindowManager()
259{
260    while(m_first)
261        close_window(m_first);
262    wm = NULL;
263}
264
265void WindowManager::add_window(AWindow *win)
266{
267    if (!m_first)
268        m_first = win;
269    else
270    {
271        AWindow *tmp = m_first;
272        while (tmp->next)
273            tmp = tmp->next;
274        tmp->next = win;
275        win->next = nullptr;
276    }
277}
278
279void WindowManager::remove_window(AWindow *win)
280{
281    if (m_grab == win)
282        m_grab = nullptr;
283
284    // close the window we were dragging
285    if(state == dragging && win == drag_window)
286        state = inputing;
287
288    if(m_first == win)
289        m_first = m_first->next;
290    else
291    {
292        AWindow * search;
293        for(search = m_first; search->next != win; search = search->next)
294            search->m_surf->AddDirty(win->m_pos - search->m_pos,
295                                     win->m_pos - search->m_pos + win->m_size);
296        search->m_surf->AddDirty(win->m_pos - search->m_pos,
297                                 win->m_pos - search->m_pos + win->m_size);
298        search->next = win->next;
299    }
300
301    m_surf->AddDirty(win->m_pos, win->m_pos + win->m_size);
302}
303
304AWindow * WindowManager::CreateWindow(ivec2 pos, ivec2 size,
305                                      String const &name,
306                                      AWidgetList const &widgets)
307{
308    if(pos.x > m_surf->Size().x - 4)
309        pos.x = m_surf->Size().x - 25;
310    if(pos.y > m_surf->Size().y - 4)
311        pos.y = m_surf->Size().y - 10;
312
313    AWindow * j = new AWindow(pos, size, name, widgets);
314    j->show();
315
316    return j;
317}
318
319void WindowManager::flush_screen()
320{
321    ivec2 m1(0, 0);
322
323    if (has_mouse())
324    {
325        m1 = m_pos - m_center;
326        ivec2 m2 = m1 + m_sprite->m_visual->Size();
327
328        m_sprite->m_save->PutPart(m_surf, ivec2(0, 0), m1, m2);
329        m_surf->PutImage(m_sprite->m_visual, m1, 1);
330    }
331
332    for (AWindow *p = m_first; p; p = p->next)
333        if (!p->is_hidden())
334            m_surf->DeleteDirty(p->m_pos, p->m_pos + p->m_size);
335    update_dirty(m_surf);
336
337    if (has_mouse())
338        m_surf->PutImage(m_sprite->m_save, m1);
339
340    for (AWindow *p = m_first; p; p = p->next)
341    {
342        if (p->is_hidden())
343            continue;
344
345        if (has_mouse())
346        {
347            m_sprite->m_save->PutPart(p->m_surf, ivec2(0, 0), m1 - p->m_pos,
348                                      m1 - p->m_pos + m_sprite->m_visual->Size());
349            p->m_surf->PutImage(m_sprite->m_visual, m1 - p->m_pos, 1);
350        }
351
352//      m_surf->DeleteDirty(p->m_pos, p->m_pos + p->m_size);
353        for (AWindow *q = p->next; q; q = q->next)
354            if (!q->is_hidden())
355                p->m_surf->DeleteDirty(q->m_pos - p->m_pos,
356                                       q->m_pos - p->m_pos + q->m_size);
357        update_dirty(p->m_surf, p->m_pos.x, p->m_pos.y);
358        if (has_mouse())
359            p->m_surf->PutImage(m_sprite->m_save, m1 - p->m_pos, 0);
360    }
361}
362
363AWindow::AWindow(String const &name)
364{
365    _x1 = left_border();
366    _y1 = jw_top + 5;
367    _x2 = _y2 = 0;
368
369    _hidden = true;
370    _moveable = true;
371    // property.flags = JWINDOW_NOAUTOHIDE_FLAG;
372
373    inm = new InputManager(this);
374    reconfigure();
375
376    m_surf = nullptr;
377    next = nullptr;
378
379    _name = strdup(name.C());
380    wm->add_window(this);
381}
382
383AWindow::AWindow(ivec2 pos, ivec2 size, String const &name, AWidgetList const &widgets)
384{
385    m_size = ivec2::zero;
386    _hidden = false;
387    _moveable = true;
388
389    _x1 = left_border();
390    _y1 = name.Count() ? top_border() : jw_top + 5;
391
392    m_surf = nullptr;
393    inm = new InputManager(m_surf, widgets);
394    reconfigure(); /* FIXME: TODO */
395
396    m_size.x = size.x >= 0 ? size.x + left_border() : m_size.x - size.x;
397    m_size.y = size.y >= 0 ? size.y + top_border() : m_size.y - size.y;
398    m_pos.x = pos.x >= 0 ? pos.x : xres - m_size.x + pos.x - left_border() - right_border() - 1;
399    m_pos.y = pos.y >= 0 ? pos.y : yres - m_size.y + pos.y - top_border() - bottom_border() - 1;
400
401    backg = wm->medium_color();
402
403    _x2 = m_size.x - 1;
404    _y2 = m_size.y - 1;
405    m_size += ivec2(right_border(), bottom_border());
406
407    if(size.x == -1)
408        m_size.x = lol::max(m_size.x, 15 + left_border() + right_border());
409    if(size.y == -1)
410        m_size.y = lol::max(m_size.y, top_border() + bottom_border());
411    m_surf = new AImage(m_size, 2);
412    // Keep this from getting destroyed when image list is cleared
413    image_list.Pop();
414    m_surf->clear(backg);
415    inm->m_surf = m_surf;
416
417    next = nullptr;
418
419    _name = strdup(name.C());
420
421    wm->add_window(this);
422    if(!wm->frame_suppress)
423        redraw();
424}
425
426AWindow::~AWindow()
427{
428    wm->remove_window(this);
429    local_close();
430    delete m_surf;
431    delete inm;
432    if(_name)
433        free(_name);
434}
435
436void AWindow::reconfigure()
437{
438    m_size = ivec2(2);
439
440    for (int i = 0; i < inm->m_fields.Count(); ++i)
441    {
442        inm->m_fields[i]->set_owner(this);
443        ibox2 area = inm->m_fields[i]->GetArea();
444        m_size = lol::max(m_size, area.B);
445    }
446}
447
448void AWindow::local_close()
449{
450    ;
451}
452
453void AWindow::redraw()
454{
455    int hi = wm->bright_color();
456    int med = wm->medium_color();
457    int low = wm->dark_color();
458    JCFont * fnt = wm->frame_font();
459
460    if(_name)
461    {
462        if (right_border() >= 1)
463        {
464            m_surf->WidgetBar(ivec2(0, 0), m_size - ivec2(1), hi, med, low);
465            if (right_border() >= 3)
466                m_surf->WidgetBar(ivec2(right_border() - 1, top_border() - 1),
467                                  m_size - ivec2(left_border(), bottom_border()),
468                                  low, med, hi);
469
470          else
471            m_surf->Line(ivec2(left_border() - 1, top_border() - 1),
472                         ivec2(right_border() - 1, top_border() - 1), low);
473        }
474      m_surf->Rectangle(ivec2(2, 2), ivec2(top_border() - 2, top_border() - 3),
475                        wm->black());
476      m_surf->WidgetBar(ivec2(3, 3), ivec2(top_border() - 3, top_border() - 4),
477                        hi, med, low); // draws the 'close' button
478    }
479
480  else
481    {
482      if (right_border() >= 1)
483        {
484          m_surf->WidgetBar(ivec2(0, 0), m_size - ivec2(1), hi, med, low);
485          if (right_border() >= 3)
486            m_surf->WidgetBar(ivec2(right_border() - 1, jw_top + 4),
487                              m_size - ivec2(left_border(), bottom_border()),
488                              low, med, hi);
489          else
490            m_surf->Line(ivec2(left_border() - 1, jw_top + 4),
491                         ivec2(right_border() - 1, jw_top + 4), low);
492        }
493      // Draw the 'close' button
494      m_surf->Rectangle(ivec2(1, 1), ivec2(4, 4), wm->black ());
495      m_surf->WidgetBar(ivec2(2, 2), ivec2(3, 3), hi, med, low);
496    }
497  if (_name && _name[0])
498    {
499      m_surf->Bar(ivec2(top_border(), 1),
500                  ivec2(top_border() + fnt->Size().x * strlen (_name) + 1,
501                        top_border() - 2),
502                  med);
503      fnt->PutString(m_surf, ivec2(top_border() + 1, 1), _name, low);
504    }
505  // clear 'client' region
506  m_surf->Bar(ivec2(x1(), y1()), ivec2(x2(), y2()), backg);
507  inm->redraw ();
508}
509
510int AWindow::left_border()
511{
512    return frame_left();
513}
514
515int AWindow::right_border()
516{
517    return frame_right();
518}
519
520int AWindow::top_border()
521{
522    return wm->font()->Size().y + frame_top();
523}
524
525int AWindow::bottom_border()
526{
527    return frame_bottom();
528}
529
530// unlinks ID from fields list and return the pointer to it
531AWidget *InputManager::unlink(int id)
532{
533    for (int i = 0; i < m_fields.Count(); ++i)
534    {
535        /* Does this item match the ID? */
536        if (m_fields[i]->m_id == id)
537        {
538            AWidget *ret = m_fields[i];
539            m_fields.Remove(i);
540
541            if (m_active == i)
542                m_active = m_fields.Count() ? 0 : -1;
543
544            return ret;
545        }
546
547        /* Maybe the item's children match the ID */
548        AWidget *child = m_fields[i]->unlink(id);
549        if (child)
550            return child;
551    }
552
553    return nullptr; // no such id
554}
555
556InputManager::~InputManager()
557{
558    for (int i = 0; i < m_fields.Count(); ++i)
559        delete m_fields[i];
560    m_fields.Empty();
561}
562
563void InputManager::clear_current()
564{
565    if (m_owner)
566        m_surf = m_owner->m_surf;
567    if (m_active >= 0)
568        m_fields[m_active]->Draw(0, m_surf);
569    m_active = -1;
570}
571
572void InputManager::handle_event(Event &ev, AWindow *j)
573{
574  int in_area = -1;
575
576  if(m_owner)
577      m_surf = m_owner->m_surf;
578
579  if (j)
580  {
581    ev.mouse_move -= j->m_pos;
582    m_cur = j;
583  }
584
585  if (m_grab == -1)
586  {
587    if ((ev.type==EV_MOUSE_BUTTON && ev.mouse_button==1) || ev.type==EV_MOUSE_MOVE)
588    {
589      for (int i = 0; i < m_fields.Count(); ++i)
590      {
591        ibox2 area = m_fields[i]->GetArea();
592        if (ev.mouse_move >= area.A && ev.mouse_move <= area.B)
593            in_area = i;
594      }
595      if (in_area != m_active && (no_selections_allowed || (in_area >= 0 && m_fields[in_area]->selectable())))
596      {
597        if (m_active >= 0)
598          m_fields[m_active]->Draw(0, m_surf);
599
600        m_active = in_area;
601
602        if (m_active >= 0)
603          m_fields[m_active]->Draw(1, m_surf);
604      }
605    }
606    if (ev.type == EV_KEY && ev.key == JK_TAB && m_active >= 0)
607    {
608      m_fields[m_active]->Draw(0, m_surf);
609      do
610      {
611        ++m_active;
612      } while (m_active < m_fields.Count() && !m_fields[m_active]->selectable());
613
614      if (m_active >= m_fields.Count())
615        m_active = -1;
616      if (m_active >= 0)
617        m_fields[m_active]->Draw(1, m_surf);
618    }
619  }
620  else
621    m_active = m_grab;
622
623  if (m_active >= 0)
624  {
625    if (ev.type!=EV_MOUSE_MOVE && ev.type!=EV_MOUSE_BUTTON)
626      m_fields[m_active]->handle_event(ev, m_surf, this);
627    else
628    {
629      ibox2 area = m_fields[m_active]->GetArea();
630      if (m_grab >= 0 || (ev.mouse_move >= area.A && ev.mouse_move <= area.B))
631          m_fields[m_active]->handle_event(ev, m_surf, j ? j->inm : this);
632    }
633  }
634
635  if (j)
636    ev.mouse_move += j->m_pos;
637}
638
639void InputManager::allow_no_selections()
640{
641    no_selections_allowed=1;
642}
643
644void InputManager::redraw()
645{
646    if (m_owner)
647        m_surf = m_owner->m_surf;
648    for (int i = 0; i < m_fields.Count(); ++i)
649        m_fields[i]->draw_first(m_surf);
650    if (m_active >= 0)
651        m_fields[m_active]->Draw(1, m_surf);
652}
653
654InputManager::InputManager(AImage *screen, AWidgetList const &fields)
655{
656    no_selections_allowed = 0;
657    m_cur = nullptr;
658    m_grab = -1;
659    m_owner = nullptr;
660    m_surf = screen;
661    m_fields = fields;
662    m_active = -1;
663    for (int i = 0; i < m_fields.Count(); ++i)
664        if (m_fields[i]->selectable())
665        {
666            m_active = i;
667            break;
668        }
669
670    if (m_surf)
671        redraw();
672}
673
674InputManager::InputManager(AWindow *owner, AWidgetList const &fields)
675{
676    no_selections_allowed = 0;
677    m_cur = nullptr;
678    m_grab = -1;
679    m_owner = owner;
680    m_surf = nullptr;
681    m_fields = fields;
682    m_active = -1;
683    for (int i = 0; i < m_fields.Count(); ++i)
684        if (m_fields[i]->selectable())
685        {
686            m_active = i;
687            break;
688        }
689}
690
691void InputManager::grab_focus(AWidget *f)
692{
693    for (int i = 0; i < m_fields.Count(); ++i)
694        if (f == m_fields[i])
695            m_grab = i;
696    if (m_cur)
697        wm->grab_focus(m_cur);
698}
699
700void InputManager::release_focus()
701{
702    m_grab = -1;
703    if (m_cur)
704        wm->release_focus();
705}
706
707void InputManager::remap(Filter *f)
708{
709    for (int i = 0; i < m_fields.Count(); ++i)
710        m_fields[i]->remap(f);
711    redraw();
712}
713
714void InputManager::Add(AWidgetList const &fields)
715{
716    if (fields[0]->selectable())
717        m_fields += fields;
718}
719
720AWidget *InputManager::get(int id)
721{
722    for (int i = 0; i < m_fields.Count(); ++i)
723    {
724        AWidget *ret = m_fields[i]->find(id);
725        if (ret)
726            return ret;
727    }
728    return nullptr;
729}
730
731AWidget::AWidget()
732  : m_pos(0, 0),
733    m_id(0),
734    m_owner(nullptr)
735{
736}
737
738AWidget::AWidget(ivec2 pos, int id)
739  : m_pos(pos),
740    m_id(id),
741    m_owner(nullptr)
742{
743}
744
745AWidget::~AWidget()
746{
747    ;
748}
749
750/* re-position the control with respect to the "client" area of the window */
751void AWidget::set_owner(AWindow * owner)
752{
753    if (m_owner)
754        Move(m_pos - ivec2(m_owner->x1(), m_owner->y1()));
755    m_owner = owner;
756    if (m_owner)
757        Move(m_pos + ivec2(m_owner->x1(), m_owner->y1()));
758}
759
Note: See TracBrowser for help on using the repository browser.