-Some cleanups in text scrolling code
[supertux.git] / src / textscroller.cpp
1 #include <config.h>
2
3 #include "textscroller.h"
4
5 #include <stdexcept>
6 #include "resources.h"
7 #include "video/font.h"
8 #include "video/drawing_context.h"
9 #include "app/globals.h"
10 #include "lisp/parser.h"
11 #include "lisp/lisp.h"
12
13 static const float DEFAULT_SPEED = 1.0;
14 static const float MAX_VEL = 10;
15 static const float SPEED_INC = 0.01;
16 static const float SCROLL = 60;
17 static const float ITEMS_SPACE = 4;
18
19 static void split_text(const std::string& text, std::vector<std::string>& lines)
20 {
21   // Split text string lines into a vector
22   lines.clear();
23   std::string::size_type i, l;
24   i = 0;
25   while(true) {
26     l = text.find("\n", i);
27
28     if(l == std::string::npos) {
29       lines.push_back(text.substr(i, text.size()-i));
30       break;
31     }
32
33     lines.push_back(text.substr(i, l-i));
34     i = l+1;
35   }
36 }
37
38 void display_text_file(const std::string& file)
39 {
40   const Font* heading_font = white_big_text;
41   const Font* normal_font = white_text;
42   const Font* small_font = white_small_text;
43   const Font* reference_font = blue_text;
44   float speed = DEFAULT_SPEED;
45   
46   std::string text;
47   std::string background_file;
48   std::vector<std::string> lines;
49
50   std::string filename = datadir + "/" + file;
51   lisp::Parser parser;
52   try {
53     std::auto_ptr<lisp::Lisp> root (parser.parse(filename));
54
55     const lisp::Lisp* text_lisp = root->get_lisp("supertux-text");
56     if(!text_lisp)
57       throw std::runtime_error("File isn't a supertux-text file");
58     
59     if(!text_lisp->get("text", text))
60       throw std::runtime_error("file doesn't contain a text field");
61     if(!text_lisp->get("background", background_file))
62       throw std::runtime_error("file doesn't contain a background file");
63     text_lisp->get("speed", speed);
64   } catch(std::exception& e) {
65     std::cerr << "Couldn't load file '" << filename << "': " << e.what() <<
66       "\n";
67     return;
68   }
69
70   // Split text string lines into a vector
71   split_text(text, lines);
72
73   // load background image
74   Surface* background = new Surface(
75       get_resource_filename("images/background/" + background_file), false);
76
77   int done = 0;
78   float scroll = 0;
79   speed /= 50.0;
80   float left_border = 50;
81
82   DrawingContext context;
83   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
84
85   Uint32 lastticks = SDL_GetTicks();
86   while(!done)
87     {
88       /* in case of input, exit */
89       SDL_Event event;
90       while(SDL_PollEvent(&event))
91         switch(event.type)
92           {
93           case SDL_KEYDOWN:
94             switch(event.key.keysym.sym)
95               {
96               case SDLK_UP:
97                 speed -= SPEED_INC;
98                 break;
99               case SDLK_DOWN:
100                 speed += SPEED_INC;
101                 break;
102               case SDLK_SPACE:
103               case SDLK_RETURN:
104                 if(speed >= 0)
105                   scroll += SCROLL;
106                 break;
107               case SDLK_ESCAPE:
108                 done = 1;
109                 break;
110               default:
111                 break;
112               }
113             break;
114           case SDL_QUIT:
115             done = 1;
116             break;
117           default:
118             break;
119           }
120
121       if(speed > MAX_VEL)
122         speed = MAX_VEL;
123       else if(speed < -MAX_VEL)
124         speed = -MAX_VEL;
125
126       /* draw the credits */
127       context.draw_surface(background, Vector(0,0), 0);
128
129       float y = 0;
130       for(size_t i = 0; i < lines.size(); i++) {
131         const std::string& line = lines[i];
132         if(line.size() == 0) {
133           y += normal_font->get_height() + ITEMS_SPACE;
134           continue;
135         }
136
137         const Font* font = 0;
138         bool center = true;
139         switch(line[0])
140         {
141           case ' ': font = small_font; break;
142           case '\t': font = normal_font; break;
143           case '-': font = heading_font; break;
144           case '*': font = reference_font; break;
145           case '#': font = normal_font; center = false; break;
146           default:
147             std::cerr << "Warning: text contains an unformated line.\n";
148             font = normal_font;
149             center = false;
150             break;
151         }
152         
153         if(center) {
154           context.draw_text(font,
155               line.substr(1, line.size()-1),
156               Vector(screen->w/2, screen->h + y - scroll),
157               CENTER_ALLIGN, LAYER_FOREGROUND1);
158         } else {
159           context.draw_text(font,
160               line.substr(1, line.size()-1),
161               Vector(left_border, screen->h + y - scroll),
162               LEFT_ALLIGN, LAYER_FOREGROUND1);
163         }
164           
165         y += font->get_height() + ITEMS_SPACE;
166       }
167
168       context.do_drawing();
169
170       if(screen->h+y-scroll < 0 && 20+screen->h+y-scroll < 0)
171         done = 1;
172
173       Uint32 ticks = SDL_GetTicks();
174       scroll += speed * (ticks - lastticks);
175       lastticks = ticks;
176       if(scroll < 0)
177         scroll = 0;
178
179       SDL_Delay(10);
180     }
181
182   SDL_EnableKeyRepeat(0, 0);    // disables key repeating
183   delete background;
184 }
185
186 InfoBox::InfoBox(const std::string& text)
187   : firstline(0)
188 {
189   split_text(text, lines);
190 }
191
192 InfoBox::~InfoBox()
193 {
194 }
195
196 void
197 InfoBox::draw(DrawingContext& context)
198 {
199   const Font* heading_font = white_big_text;
200   const Font* normal_font = white_text;
201   const Font* small_font = white_small_text;
202   const Font* reference_font = blue_text;
203   
204   float x1 = 200;
205   float y1 = 100;
206   float width = 400;
207   float height = 200;
208   
209   context.draw_filled_rect(Vector(x1, y1), Vector(width, height),
210       Color(150, 180, 200, 125), LAYER_GUI-1);
211
212   float y = y1;
213   for(size_t i = firstline; i < lines.size(); ++i) {
214     const std::string& line = lines[i];
215     if(y >= y1 + height)
216       break;
217
218     if(line.size() == 0) {
219       y += normal_font->get_height() + ITEMS_SPACE;    
220       continue;                                        
221     }
222
223     const Font* font = 0;
224     bool center = true;
225     switch(line[0])
226     {
227       case ' ': font = small_font; break;
228       case '\t': font = normal_font; break;
229       case '-': font = heading_font; break;
230       case '*': font = reference_font; break;
231       case '#': font = normal_font; center = false; break;
232       default:
233         std::cerr << "Warning: text contains an unformated line.\n";
234         font = normal_font;
235         center = false;
236         break;
237     }
238     
239     if(center) {
240       context.draw_text(font,
241           line.substr(1, line.size()-1),
242           Vector(screen->w/2, y),
243           CENTER_ALLIGN, LAYER_GUI);
244     } else {
245       context.draw_text(font,
246           line.substr(1, line.size()-1),
247           Vector(x1, y),
248           LEFT_ALLIGN, LAYER_GUI);
249     }
250       
251     y += font->get_height() + ITEMS_SPACE;
252   }
253 }
254
255 void
256 InfoBox::scrollup()
257 {
258   if(firstline > 0)
259     firstline--;
260 }
261
262 void
263 InfoBox::scrolldown()
264 {
265   if(firstline < lines.size()-1)
266     firstline++;
267 }
268
269 void
270 InfoBox::pageup()
271 {
272 }
273
274 void
275 InfoBox::pagedown()
276 {
277 }
278