/* * Copyright (C) 2003 Daniel Heck * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * $Id: menus_internal.hh,v 1.1 2003/09/14 19:40:10 dheck Exp $ */ //---------------------------------------------------------------------- // Helper classes //---------------------------------------------------------------------- namespace { class BuildVList { Rect r; Menu *container; int skip; public: BuildVList(Menu *cc, const px::Rect &rr, int s) : r(rr), container(cc), skip(s) {} Widget *add(Widget *w) { container->add(w, r); r.y += r.h+skip; return w; } Rect pos() const { return r; } }; class VTableBuilder { Menu *m_menu; px::Rect m_widgetsize; int m_hspacing, m_vspacing; vector m_widgets; int m_maxheight; public: VTableBuilder (Menu *menu, const px::Rect &widgetsize, int vspacing, int hspacing, int max_height) : m_menu(menu), m_widgetsize(widgetsize) { m_hspacing = hspacing; m_vspacing = vspacing; m_maxheight = max_height; } Widget *add (Widget *w) { m_widgets.push_back(w); return w; } void finish() { if (m_widgets.empty()) return; int ncolumns = m_widgets.size() / m_maxheight; int nrows = (m_widgets.size() + ncolumns - 1) / ncolumns; int i=0; int x=0; for (int col = 0; col < ncolumns; col++) { int y=0; for (int row=0; row < nrows; row++, i++) { if (i == (int)m_widgets.size()) goto done; px::Rect rr(x, y, m_widgetsize.w, m_widgetsize.h); m_menu->add(m_widgets[i], rr); // m_widgets[i]->move (x, y); y += m_widgetsize.h + m_vspacing; } x += m_widgetsize.w + m_hspacing; } done: return; } }; class BuildHList { Rect r; Menu *container; int skip; public: BuildHList(Menu *cc, const px::Rect &rr, int s) : r(rr), container(cc), skip(s) {} Widget * add(Widget *w) { container->add(w, r); r.x += r.w+skip; return w; } Widget *add (Widget *w, int width) { px::Rect rr(r.x, r.y, width, r.h); container->add(w, rr); r.x += width + skip; return w; } Rect pos() const { return r; } }; } namespace { class ImageAlloc { public: px::Surface *acquire(const std::string &name) { return px::LoadImage(name.c_str()); } void release(px::Surface *s) { delete s; } }; typedef Cache ImageCache; class LevelPreviewCacheElem { Surface *surface; unsigned idx; LevelPreviewCacheElem(const LevelPreviewCacheElem&); LevelPreviewCacheElem& operator = (const LevelPreviewCacheElem& other); public: LevelPreviewCacheElem(LevelPack *lp, unsigned idx_, int xsize, int ysize) : surface(0) , idx(idx_) { surface = LevelPreview(lp, idx); // do not free, points to BackBuffer if (surface) { const Rect& game_area = display::GetGameArea(); Surface *game_surface = Grab(surface, game_area); if (game_surface) { surface = game_surface->zoom(xsize, ysize); delete game_surface; } else { surface = 0; // avoid delete } } // fprintf(stderr, "Created preview for #%i. surface=%p\n", idx, surface); } ~LevelPreviewCacheElem() { delete surface; } bool operator<(const LevelPreviewCacheElem& other) { return idx PreviewMap; PreviewMap cache; int xsize, ysize; void release() { PreviewMap::iterator i = cache.begin(); PreviewMap::iterator e = cache.end(); for (; i != e; ++i) delete i->second; } public: LevelPreviewCache() : xsize(0), ysize(0) {} ~LevelPreviewCache() { release(); } void clear() { release(); cache.clear(); } void set_size(int xs, int ys) { xsize = xs; ysize = ys; } Surface *getPreview(LevelPack *lp, unsigned idx) { PreviewMap::iterator i = cache.find(idx); if (i != cache.end()) return i->second->get_surface(); assert(xsize != 0 && ysize != 0); // forgot to call set_size() ? LevelPreviewCacheElem *ce = new LevelPreviewCacheElem(lp, idx, xsize, ysize); Surface *surface = ce->get_surface(); if (!surface) delete ce; // dont add broken levels to cache else cache[idx] = ce; return surface; } }; class LevelPackMenu : public Menu { public: LevelPackMenu(); void on_action(Widget *w); void draw_background(px::GC &gc); int get_selection() const { return m_selection; } private: vector buttons; int m_selection; }; class LevelMenu; class LevelWidget : public Widget { public: LevelWidget(LevelPack *lp, int w, int h); bool manage (); int selected_level() const { return iselected; } // Widget interface. void draw(px::GC &gc, const px::Rect &r); void set_listener(ActionListener *al) { listener = al; } void trigger_action() { cache.clear(); if (listener) { listener->on_action(this); recalc_available(); } } void change_levelpack (LevelPack *lp); void page_up(); void page_down(); void start() { set_selected (0,0); } void end() { set_current(level_pack->size()-1); } void set_current(int newsel) { set_selected(ifirst, newsel); } void next_unsolved(); bool on_event(const SDL_Event &e); LevelMenu *get_menu(); int get_position() const { return (ifirst << 4) | (iselected-ifirst); } void set_position(int pos) { int new_first = pos >> 4; int off = pos&0xf; int new_sel = new_first+off; assert(off >= 0 && off < 12); // 12 levels per page set_selected(new_first, new_sel); } void show_text(const string& text); private: // Private functions. void scroll_up(int lines); void scroll_down(int lines); void set_selected (int newfirst, int newsel); void recalc_available (); // Event handling. bool handle_keydown (const SDL_Event *e); bool handle_mousedown (const SDL_Event *e); /* ** Variables. */ ImageCache cache; LevelPack *level_pack; // The current level pack LevelPreviewCache preview_cache; int ifirst; // Index of "upper left" level int iselected; // Index of selected level int max_available; // Index of the last available level (one can choose out of x unsolved levels) int width, height; vector m_areas; // Screen areas occupied by level previews ActionListener *listener; }; class LevelMenu : public Menu { public: LevelMenu(LevelPack *lp, unsigned long pos); // rotate through levelpacks void next_levelpack() { unsigned next_pack = m_ilevelpack+1; if (next_pack == enigma::LevelPacks.size()) next_pack = 0; set_levelpack(next_pack); } void previous_levelpack() { unsigned prev_pack = m_ilevelpack; if (prev_pack == 0) prev_pack = enigma::LevelPacks.size()-1; else --prev_pack; set_levelpack(prev_pack); } int get_position() const { return (m_ilevelpack << 16) | (levelwidget->get_position() & 0xffff); } void set_position(int pos) { set_levelpack(pos >> 16); levelwidget->set_position(pos & 0xffff); } void show_text(const string& text) { shown_text = text; shown_text_ttl = 2.0; // show for two seconds } private: void update_info(); void set_levelpack (unsigned index); // Menu interface. void tick (double time); void draw_background(px::GC &gc); // Widget interface. bool on_event (const SDL_Event &e); // ActionListener interface. void on_action(Widget *w); // Variables. Widget *pgup, *pgdown, *start, *end, *unsolved; Widget *newgame; Widget *back; Widget *m_nextlp, *m_prevlp; // Next/previous level pack TextButton *m_lpbutton; // Current level pack Label *m_infobutton; Label *m_levelinfo, *m_levelinfo2; LevelWidget *levelwidget; LevelPack *level_pack; unsigned m_ilevelpack; string shown_text; // info text (disappears automatically) double shown_text_ttl; // rest duration for shown_text }; class MainMenu : public Menu { public: MainMenu(); private: // Menu interface void draw_background(px::GC &gc); // ActionListener interface. void on_action(Widget *w); // Private methods. void show_credits(); void show_help(); void show_text( const char *text[]); // Variables. Widget *m_startgame; Widget *leveled; Widget *manual; Widget *options; Widget *credits; Widget *quit; Widget *lpack; }; class ValueButton: public TextButton { int min_value; int max_value; bool update_value(int old_value, int new_value); public: ValueButton(const std::string &t, int min_value_, int max_value_) : TextButton(t, this), min_value(min_value_), max_value(max_value_) {} virtual int get_value() const = 0; virtual void set_value(int value) = 0; virtual string build_text(int value) const = 0; bool inc_value(int offset) { int old_value = get_value(); return update_value(old_value, old_value+offset); } void update() { update_value(-1, get_value()); } // Widget interface virtual bool on_event(const SDL_Event &e); virtual void on_action(Widget *w); }; }