1 /*
2 * Copyright (c) 1997 - 2003 Hansj�rg Malthaner
3 *
4 * This file is part of the Simutrans project under the artistic license.
5 * (see license.txt)
6 */
7
8 /*
9 * Sub-window for Sim
10 * keine Klasse, da die funktionen von C-Code aus aufgerufen werden koennen
11 *
12 * The function implements a WindowManager 'Object'
13 * There's only one WindowManager
14 *
15 * 17.11.97, Hj. Malthaner
16 *
17 * Window now typified
18 * 21.06.98, Hj. Malthaner
19 */
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24
25 #include "../simcolor.h"
26 #include "../simevent.h"
27 #include "../display/simgraph.h"
28 #include "../display/viewport.h"
29 #include "../simmenu.h"
30 #include "../simskin.h"
31 #include "../simsys.h"
32 #include "../simticker.h"
33 #include "simwin.h"
34 #include "../simintr.h"
35 #include "../simhalt.h"
36 #include "../simworld.h"
37
38 #include "../dataobj/translator.h"
39 #include "../dataobj/environment.h"
40 #include "../dataobj/loadsave.h"
41 #include "../dataobj/tabfile.h"
42
43 #include "../descriptor/skin_desc.h"
44
45 #include "../obj/zeiger.h"
46
47
48 #include "../player/simplay.h"
49 #include "../tpl/inthashtable_tpl.h"
50 #include "../tpl/vector_tpl.h"
51 #include "../utils/simstring.h"
52 #include "../utils/cbuffer_t.h"
53
54 #include "map_frame.h"
55 #include "help_frame.h"
56 #include "messagebox.h"
57 #include "gui_frame.h"
58
59 // needed to restore/save them
60 #include "tool_selector.h"
61 #include "player_frame_t.h"
62 #include "money_frame.h"
63 #include "halt_info.h"
64 #include "convoi_detail_t.h"
65 #include "convoi_info_t.h"
66 #include "schedule_gui.h"
67 #include "line_management_gui.h"
68 #include "schedule_list.h"
69 #include "city_info.h"
70 #include "message_frame_t.h"
71 #include "message_option_t.h"
72 #include "fabrik_info.h"
73 #include "themeselector.h"
74 #include "goods_frame_t.h"
75 #include "loadfont_frame.h"
76 #include "scenario_info.h"
77 #include "depot_frame.h"
78
79 #include "../simversion.h"
80
81 class inthashtable_tpl<ptrdiff_t,scr_coord> old_win_pos;
82
83 // hash-table: magic number to windowsize
84 class inthashtable_tpl<ptrdiff_t, scr_size> saved_windowsizes;
85
86 #define dragger_size 12
87
88 // (Mathew Hounsell)
89 // I added a button to the map window to fix it's size to the best one.
90 // This struct is the flow back to the object of the refactoring.
91 class simwin_gadget_flags_t
92 {
93 public:
simwin_gadget_flags_t()94 simwin_gadget_flags_t( ) : title(true), close( false ), help( false ), prev( false ), size( false ), next( false ), sticky( false ), gotopos( false ) { }
95
96 bool title:1;
97 bool close:1;
98 bool help:1;
99 bool prev:1;
100 bool size:1;
101 bool next:1;
102 bool sticky:1;
103 bool gotopos:1;
104 };
105
106 class simwin_t
107 {
108 public:
109 scr_coord pos; // Window position
110 uint32 dauer; // How long should the window stay open?
111 uint8 wt; // the flags for the window type
112 ptrdiff_t magic_number; // either magic number or this pointer (which is unique too)
113 gui_frame_t *gui;
114 uint16 gadget_state; // which buttons to hilite
115 bool sticky; // true if window is sticky
116 bool rollup;
117 bool dirty;
118
119 simwin_gadget_flags_t flags; // (Mathew Hounsell) See Above.
120
simwin_t()121 simwin_t() : flags() {}
122
123 bool operator== (const simwin_t &) const;
124 };
125
operator ==(const simwin_t & other) const126 bool simwin_t::operator== (const simwin_t &other) const { return gui == other.gui; }
127
128
129 #define MAX_WIN (64)
130 static vector_tpl<simwin_t> wins(MAX_WIN);
131 static vector_tpl<simwin_t> kill_list(MAX_WIN);
132
133 static karte_t* wl = NULL; // Pointer to current world is set in win_set_world
134
135 static int top_win(int win, bool keep_state );
136 static void display_win(int win);
137
138
139 // Hajo: tooltip data
140 static int tooltip_xpos = 0;
141 static int tooltip_ypos = 0;
142 static const char * tooltip_text = 0;
143 static const char * static_tooltip_text = 0;
144 // Knightly : For timed tooltip with initial delay and finite visible duration.
145 // Valid owners are required for timing. Invalid (NULL) owners disable timing.
146 static const void * tooltip_owner = 0; // owner of the registered tooltip
147 static const void * tooltip_group = 0; // group to which the owner belongs
148 static uint32 tooltip_register_time = 0; // time at which a tooltip is initially registered
149
150 static bool show_ticker=0;
151
152 /* Hajo: if we are inside the event handler,
153 * the window handler has gui pointer as value,
154 * to defer destruction if this window
155 */
156 static void *inside_event_handling = NULL;
157
158 // only this gui element can set a tooltip
159 static void *tooltip_element = NULL;
160
161 static bool destroy_framed_win(simwin_t *win);
162
163 //=========================================================================
164 // Helper Functions
165
166 #define REVERSE_GADGETS (!env_t::window_buttons_right)
167
168 /**
169 * Display a window gadget
170 * @author Hj. Malthaner
171 */
display_gadget_box(sint8 code,int const x,int const y,PIXVAL lighter,PIXVAL darker,bool const pushed)172 static int display_gadget_box(sint8 code,
173 int const x, int const y,
174 PIXVAL lighter, PIXVAL darker,
175 bool const pushed)
176 {
177
178 // If we have a skin, get gadget image data
179 const image_t *img = NULL;
180 if( skinverwaltung_t::gadget ) {
181 // "x", "?", "=", "�", "�"
182 img = skinverwaltung_t::gadget->get_image(code);
183 }
184
185 if(pushed) {
186 // mark_rect_dirty_wc(x, y, D_GADGET_WIDTH, D_TITLEBAR_HEIGHT);
187 display_fillbox_wh_clip_rgb(x, y, D_GADGET_WIDTH, D_TITLEBAR_HEIGHT, lighter, false);
188 }
189
190 // Do we have a gadget image?
191 if( img != NULL ) {
192
193 // Max Kielland: This center the gadget image and compensates for any left/top margins within the image to be backward compatible with older PAK sets.
194 display_color_img(img->imageid, x-img->x + D_GET_CENTER_ALIGN_OFFSET(img->w,D_GADGET_WIDTH), y, 0, false, false);
195
196 }
197 else {
198 const char *gadget_text = "#";
199 static const char *gadget_texts[5]={ "X", "?", "=", "<", ">" };
200
201 if( code <= SKIN_BUTTON_NEXT ) {
202 gadget_text = gadget_texts[code];
203 }
204 else if( code == SKIN_GADGET_GOTOPOS ) {
205 gadget_text = "*";
206 }
207 else if( code == SKIN_GADGET_NOTPINNED ) {
208 gadget_text = "s";
209 }
210 else if( code == SKIN_GADGET_PINNED ) {
211 gadget_text = "S";
212 }
213 display_proportional_rgb( x+4, y+4, gadget_text, ALIGN_LEFT, color_idx_to_rgb(COL_BLACK), false );
214 }
215
216 int side = x+REVERSE_GADGETS*D_GADGET_WIDTH-1;
217 display_vline_wh_clip_rgb(side+1, y+1, D_TITLEBAR_HEIGHT-2, lighter, false);
218 display_vline_wh_clip_rgb(side, y+1, D_TITLEBAR_HEIGHT-2, darker, false);
219
220 // Hajo: return width of gadget
221 return D_GADGET_WIDTH;
222 }
223
224
225 //-------------------------------------------------------------------------
226 // (Mathew Hounsell) Created
display_gadget_boxes(simwin_gadget_flags_t * flags,int x,int y,PIXVAL lighter,PIXVAL darker,uint16 gadget_state,bool sticky_pushed,bool goto_pushed)227 static int display_gadget_boxes(
228 simwin_gadget_flags_t* flags,
229 int x, int y,
230 PIXVAL lighter,
231 PIXVAL darker,
232 uint16 gadget_state,
233 bool sticky_pushed,
234 bool goto_pushed
235 ) {
236 int width = 0;
237 const int k=(REVERSE_GADGETS?1:-1);
238
239 // Only the close and sticky gadget can be pushed.
240 if( flags->close ) {
241 width += k*display_gadget_box( SKIN_GADGET_CLOSE, x + width, y, lighter, darker, gadget_state & (1<<SKIN_GADGET_CLOSE) );
242 }
243 if( flags->size ) {
244 width += k*display_gadget_box( SKIN_GADGET_MINIMIZE, x + width, y, lighter, darker, gadget_state & (1<<SKIN_GADGET_MINIMIZE) );
245 }
246 if( flags->help ) {
247 width += k*display_gadget_box( SKIN_GADGET_HELP, x + width, y, lighter, darker, gadget_state & (1<<SKIN_GADGET_HELP) );
248 }
249 if( flags->prev ) {
250 width += k*display_gadget_box( SKIN_BUTTON_PREVIOUS, x + width, y, lighter, darker, gadget_state & (1<<SKIN_BUTTON_PREVIOUS) );
251 }
252 if( flags->next ) {
253 width += k*display_gadget_box( SKIN_BUTTON_NEXT, x + width, y, lighter, darker, gadget_state & (1<<SKIN_BUTTON_NEXT) );
254 }
255 if( flags->gotopos ) {
256 width += k*display_gadget_box( SKIN_GADGET_GOTOPOS, x + width, y, lighter, darker, goto_pushed || (gadget_state & (1<<SKIN_GADGET_GOTOPOS)) );
257 }
258 if( flags->sticky ) {
259 width += k*display_gadget_box( sticky_pushed ? SKIN_GADGET_PINNED : SKIN_GADGET_NOTPINNED, x + width, y, lighter, darker, gadget_state & (1<<SKIN_GADGET_NOTPINNED) );
260 }
261
262 return abs( width );
263 }
264
265
decode_gadget_boxes(simwin_gadget_flags_t const * const flags,int const x,int const px)266 static sint8 decode_gadget_boxes(
267 simwin_gadget_flags_t const * const flags,
268 int const x,
269 int const px
270 ) {
271 int offset = px-x;
272 const int w=(REVERSE_GADGETS?-D_GADGET_WIDTH:D_GADGET_WIDTH);
273
274 //DBG_MESSAGE("simwin_gadget_et decode_gadget_boxes()","offset=%i, w=%i",offset, w );
275
276 // Only the close gadget can be pushed.
277 if( flags->close ) {
278 if( offset >= 0 && offset<D_GADGET_WIDTH ) {
279 //DBG_MESSAGE("simwin_gadget_et decode_gadget_boxes()","close" );
280 return SKIN_GADGET_CLOSE;
281 }
282 offset += w;
283 }
284 if( flags->size ) {
285 if( offset >= 0 && offset<D_GADGET_WIDTH ) {
286 //DBG_MESSAGE("simwin_gadget_et decode_gadget_boxes()","size" );
287 return SKIN_GADGET_MINIMIZE;
288 }
289 offset += w;
290 }
291 if( flags->help ) {
292 if( offset >= 0 && offset<D_GADGET_WIDTH ) {
293 //DBG_MESSAGE("simwin_gadget_et decode_gadget_boxes()","help" );
294 return SKIN_GADGET_HELP;
295 }
296 offset += w;
297 }
298 if( flags->prev ) {
299 if( offset >= 0 && offset<D_GADGET_WIDTH ) {
300 return SKIN_BUTTON_PREVIOUS;
301 }
302 offset += w;
303 }
304 if( flags->next ) {
305 if( offset >= 0 && offset<D_GADGET_WIDTH ) {
306 return SKIN_BUTTON_NEXT;
307 }
308 offset += w;
309 }
310 if( flags->gotopos ) {
311 if( offset >= 0 && offset<D_GADGET_WIDTH ) {
312 return SKIN_GADGET_GOTOPOS;
313 }
314 offset += w;
315 }
316 if( flags->sticky ) {
317 if( offset >= 0 && offset<D_GADGET_WIDTH ) {
318 return SKIN_GADGET_NOTPINNED;
319 }
320 offset += w;
321 }
322 return SKIN_GADGET_COUNT;
323 }
324
325 //-------------------------------------------------------------------------
326 // (Mathew Hounsell) Re-factored
win_draw_window_title(const scr_coord pos,const scr_size size,const FLAGGED_PIXVAL title_color,const char * const text,const FLAGGED_PIXVAL text_color,const koord3d welt_pos,const uint16 gadget_state,const bool sticky,const bool goto_pushed,simwin_gadget_flags_t & flags)327 static void win_draw_window_title(const scr_coord pos, const scr_size size,
328 const FLAGGED_PIXVAL title_color,
329 const char * const text,
330 const FLAGGED_PIXVAL text_color,
331 const koord3d welt_pos,
332 const uint16 gadget_state,
333 const bool sticky,
334 const bool goto_pushed,
335 simwin_gadget_flags_t &flags )
336 {
337 PUSH_CLIP_FIT(pos.x, pos.y, size.w, size.h);
338
339 PIXVAL lighter = display_blend_colors(title_color, color_idx_to_rgb(COL_WHITE), 25);
340 PIXVAL darker = display_blend_colors(title_color, color_idx_to_rgb(COL_BLACK), 25);
341
342 display_fillbox_wh_clip_rgb(pos.x, pos.y, size.w, D_TITLEBAR_HEIGHT, title_color, false);
343
344 display_fillbox_wh_clip_rgb( pos.x + 1, pos.y, size.w - 2, 1, lighter, false );
345 display_fillbox_wh_clip_rgb( pos.x + 1, pos.y + D_TITLEBAR_HEIGHT - 1, size.w - 2, 1, darker, false );
346
347 display_vline_wh_clip_rgb( pos.x, pos.y, D_TITLEBAR_HEIGHT, lighter, false );
348 display_vline_wh_clip_rgb( pos.x + size.w - 1, pos.y, D_TITLEBAR_HEIGHT, darker, false );
349
350 // Draw the gadgets and then move left and draw text.
351 flags.gotopos = (welt_pos != koord3d::invalid);
352 int width = display_gadget_boxes( &flags, pos.x+(REVERSE_GADGETS?0:size.w-D_GADGET_WIDTH), pos.y, lighter, darker, gadget_state, sticky, goto_pushed );
353 int titlewidth = display_proportional_clip_rgb( pos.x + (REVERSE_GADGETS?width+4:4), pos.y+(D_TITLEBAR_HEIGHT-LINEASCENT)/2, text, ALIGN_LEFT, text_color, false );
354 if( flags.gotopos ) {
355 display_proportional_clip_rgb( pos.x + (REVERSE_GADGETS?width+4:4)+titlewidth+8, pos.y+(D_TITLEBAR_HEIGHT-LINEASCENT)/2, welt_pos.get_2d().get_fullstr(), ALIGN_LEFT, text_color, false );
356 }
357 POP_CLIP();
358 }
359
360
361 //-------------------------------------------------------------------------
362
363 /**
364 * Draw dragger widget
365 * @author Hj. Malthaner
366 */
win_draw_window_dragger(scr_coord pos,scr_size size)367 static void win_draw_window_dragger(scr_coord pos, scr_size size)
368 {
369 pos += size;
370 if( skinverwaltung_t::gadget && skinverwaltung_t::gadget->get_image_id(SKIN_WINDOW_RESIZE)!=IMG_EMPTY ) {
371 const image_t *dragger = skinverwaltung_t::gadget->get_image(SKIN_WINDOW_RESIZE);
372 display_color_img( dragger->get_id(), pos.x-dragger->get_pic()->w, pos.y-dragger->get_pic()->h, 0, false, false);
373 }
374 else {
375 for( int x=0; x<dragger_size; x++ ) {
376 display_fillbox_wh_clip_rgb( pos.x-x, pos.y-dragger_size+x, x, 1, color_idx_to_rgb((x & 1) ? COL_BLACK : MN_GREY4), true);
377 }
378 }
379 }
380
381 // Functions to save & restore windowsize
382
guess_magic_number(simwin_t * win,ptrdiff_t magic=magic_none)383 ptrdiff_t guess_magic_number(simwin_t *win, ptrdiff_t magic = magic_none)
384 {
385 if (win) {
386 magic = win->magic_number;
387 }
388 // reduce player-wise magic numbers and magic numbers derived from handles
389 const ptrdiff_t magic_pl[] = {
390 magic_finances_t, magic_convoi_list, magic_convoi_list_filter, magic_line_list, magic_halt_list,
391 magic_line_management_t, magic_ai_options_t, magic_ai_selector, magic_pwd_t, magic_jump, magic_headquarter, magic_headquarter + MAX_PLAYER_COUNT,
392 magic_none,
393 magic_convoi_info, magic_halt_info, magic_toolbar,
394 };
395
396 for (uint i=1; i<lengthof(magic_pl); i++) {
397 if (magic_pl[i-1] == magic_none) {
398 continue;
399 }
400 if (magic_pl[i-1] <= magic && magic < magic_pl[i]) {
401 return magic_pl[i-1];
402 }
403 }
404 if (win == NULL) {
405 return magic;
406 }
407 // try rdwr_id
408 uint32 id = win->gui->get_rdwr_id();
409 if (id != magic_reserved) {
410 return id;
411 }
412
413 // anything outside the magic number's ranges will be ignored
414 if (magic <= magic_reserved || magic >= magic_max) {
415 magic = magic_none;
416 }
417 return magic;
418 }
419
save_windowsize(simwin_t * win)420 void save_windowsize(simwin_t *win)
421 {
422 ptrdiff_t magic = guess_magic_number(win);
423 if (magic != magic_none) {
424 saved_windowsizes.set(magic, win->gui->get_windowsize() );
425 }
426 }
427
get_stored_windowsize(simwin_t * win)428 scr_size get_stored_windowsize(simwin_t *win)
429 {
430 ptrdiff_t magic = guess_magic_number(win);
431 if (magic != magic_none) {
432 return saved_windowsizes.get(magic);
433 }
434 return scr_size();
435 }
436
rdwr_win_settings(loadsave_t * file)437 void rdwr_win_settings(loadsave_t *file)
438 {
439 if (file->is_loading()) {
440 saved_windowsizes.clear();
441 sint64 magic;
442 do {
443 file->rdwr_longlong(magic);
444 if (magic != magic_none) {
445 scr_size s;
446 file->rdwr_long(s.w);
447 file->rdwr_long(s.h);
448 // ignore stuff that will not be used anymore
449 if (magic == guess_magic_number(NULL, magic)) {
450 saved_windowsizes.put(magic, s);
451 }
452 }
453 } while (magic != magic_none);
454 }
455 else {
456 typedef inthashtable_tpl<ptrdiff_t, scr_size> stupid_table_t;
457 FOR(stupid_table_t, it, saved_windowsizes) {
458 sint64 m = it.key;
459 file->rdwr_longlong(m);
460 file->rdwr_long(it.value.w);
461 file->rdwr_long(it.value.h);
462 }
463 sint64 m = magic_none;
464 file->rdwr_longlong(m);
465 }
466 }
467 //=========================================================================
468
469
470
471 // returns the window (if open) otherwise zero
win_get_magic(ptrdiff_t magic)472 gui_frame_t *win_get_magic(ptrdiff_t magic)
473 {
474 if( magic!=-1 && magic!=0 ) {
475 // there is at most one window with a positive magic number
476 FOR( vector_tpl<simwin_t>, const& i, wins ) {
477 if( i.magic_number == magic ) {
478 // if 'special' magic number, return it
479 return i.gui;
480 }
481 }
482 }
483 return NULL;
484 }
485
486
487 // sets the magic of a gui_frame_t (needed during reload of windows)
win_set_magic(gui_frame_t * gui,ptrdiff_t magic)488 bool win_set_magic( gui_frame_t *gui, ptrdiff_t magic )
489 {
490 if( magic!=-1 && magic!=0 ) {
491 // there is at most one window with a positive magic number
492 FOR( vector_tpl<simwin_t>, &i, wins ) {
493 if( i.gui == gui ) {
494 i.magic_number = magic;
495 return true;
496 }
497 }
498 }
499 return false;
500 }
501
502
503 /**
504 * Returns top window
505 * @author prissi
506 */
win_get_top()507 gui_frame_t *win_get_top()
508 {
509 return wins.empty() ? 0 : wins.back().gui;
510 }
511
512
513 /**
514 * returns the focused component of the top window
515 * @author Knightly
516 */
win_get_focus()517 gui_component_t *win_get_focus()
518 {
519 return wins.empty() ? 0 : wins.back().gui->get_focus();
520 }
521
522
win_get_open_count()523 int win_get_open_count()
524 {
525 return wins.get_count();
526 }
527
528
529 // brings a window to front, if open
top_win(const gui_frame_t * gui,bool keep_rollup)530 bool top_win( const gui_frame_t *gui, bool keep_rollup )
531 {
532 for( uint i=0; i<wins.get_count()-1; i++ ) {
533 if(wins[i].gui==gui) {
534 top_win(i,keep_rollup);
535 return true;
536 }
537 }
538 // not open
539 return false;
540 }
541
542
543 /**
544 * Checks if a window is a top level window
545 * @author Hj. Malthaner
546 */
win_is_top(const gui_frame_t * ig)547 bool win_is_top(const gui_frame_t *ig)
548 {
549 return !wins.empty() && wins.back().gui == ig;
550 }
551
552
553 // window functions
554
555
556 // save/restore all dialogues
rdwr_all_win(loadsave_t * file)557 void rdwr_all_win(loadsave_t *file)
558 {
559 if( file->is_version_atleast(120, 8) ) {
560 if( file->is_saving() ) {
561 FOR(vector_tpl<simwin_t>, & i, wins) {
562 uint32 id = i.gui->get_rdwr_id();
563 if( id!=magic_reserved ) {
564 file->rdwr_long( id );
565 i.pos.rdwr(file);
566 file->rdwr_byte(i.wt);
567 file->rdwr_bool(i.sticky);
568 file->rdwr_bool(i.rollup);
569 i.gui->rdwr(file);
570 }
571 }
572 uint32 end = magic_none;
573 file->rdwr_long( end );
574 }
575 else {
576 // restore windows
577 while(1) {
578 uint32 id;
579 file->rdwr_long(id);
580 // create the matching
581 gui_frame_t *w = NULL;
582 switch(magic_numbers(id)) {
583
584 // end of dialogues
585 case magic_none: return;
586
587 // actual dialogues to restore
588 case magic_convoi_info: w = new convoi_info_t(); break;
589 case magic_themes: w = new themeselector_t(); break;
590 case magic_halt_info: w = new halt_info_t(); break;
591 case magic_reliefmap: w = new map_frame_t(); break;
592 case magic_ki_kontroll_t: w = new ki_kontroll_t(); break;
593 case magic_schedule_rdwr_dummy: w = new schedule_gui_t(); break;
594 case magic_line_schedule_rdwr_dummy: w = new line_management_gui_t(); break;
595 case magic_city_info_t: w = new city_info_t(); break;
596 case magic_messageframe: w = new message_frame_t(); break;
597 case magic_message_options: w = new message_option_t(); break;
598 case magic_factory_info: w = new fabrik_info_t(); break;
599 case magic_goodslist: w = new goods_frame_t(); break;
600 case magic_font: w = new loadfont_frame_t(); break;
601 case magic_scenario_info: w = new scenario_info_t(); break;
602 case magic_depot: w = new depot_frame_t(); break;
603
604 default:
605 if( id>=magic_finances_t && id<magic_finances_t+MAX_PLAYER_COUNT ) {
606 w = new money_frame_t( wl->get_player(id-magic_finances_t) );
607 }
608 else if( id>=magic_line_management_t && id<magic_line_management_t+MAX_PLAYER_COUNT ) {
609 w = new schedule_list_gui_t( wl->get_player(id-magic_line_management_t) );
610 }
611 else if( id>=magic_toolbar && id<magic_toolbar+256 ) {
612 tool_t::toolbar_tool[id-magic_toolbar]->update(wl->get_active_player());
613 w = tool_t::toolbar_tool[id-magic_toolbar]->get_tool_selector();
614 }
615 else {
616 dbg->error( "rdwr_all_win()", "No idea how to restore magic 0x%X", id );
617 return;
618 }
619 }
620 /* sequence is now the same for all dialogues
621 * restore coordinates
622 * create window
623 * read state
624 * restore content
625 * restore state - gui_frame_t::rdwr() might create its own window ->> want to restore state to that window
626 */
627 scr_coord p;
628 p.rdwr(file);
629 uint8 win_type;
630 file->rdwr_byte( win_type );
631 create_win( p.x, p.y, w, (wintype)win_type, id );
632 bool sticky, rollup;
633 file->rdwr_bool( sticky );
634 file->rdwr_bool( rollup );
635 // now load the window
636 uint32 count = wins.get_count();
637 w->rdwr( file );
638
639 // restore sticky / rollup status
640 // ensure that the new status is to currently loaded window
641 if (wins.get_count() >= count) {
642 wins.back().sticky = sticky;
643 wins.back().rollup = rollup;
644 }
645 }
646 }
647 }
648 }
649
650
create_win(gui_frame_t * const gui,wintype const wt,ptrdiff_t const magic)651 int create_win(gui_frame_t* const gui, wintype const wt, ptrdiff_t const magic)
652 {
653 return create_win( -1, -1, gui, wt, magic);
654 }
655
656
create_win(int x,int y,gui_frame_t * const gui,wintype const wt,ptrdiff_t const magic)657 int create_win(int x, int y, gui_frame_t* const gui, wintype const wt, ptrdiff_t const magic)
658 {
659 assert(gui!=NULL && magic!=0);
660
661 if( gui_frame_t *win = win_get_magic(magic) ) {
662 if( env_t::second_open_closes_win ) {
663 destroy_win( win );
664 if( !( wt & w_do_not_delete ) ) {
665 delete gui;
666 }
667 }
668 else {
669 top_win( win );
670 }
671 return -1;
672 }
673
674 if( x==-1 && y==-1 && env_t::remember_window_positions ) {
675 // look for window in hash table
676 if( scr_coord *k = old_win_pos.access(magic) ) {
677 x = k->x;
678 y = k->y;
679 }
680 }
681
682 /* if there are too many handles (likely in large games)
683 * we search for any error/news message at the bottom of the stack and delete it
684 * => newer information might be more important ...
685 */
686 if( wins.get_count()==MAX_WIN ) {
687 // we try to remove one of the lowest news windows (magic_none)
688 for( uint i=0; i<MAX_WIN; i++ ) {
689 if( wins[i].magic_number == magic_none && dynamic_cast<news_window *>(wins[i].gui)!=NULL ) {
690 destroy_win( wins[i].gui );
691 break;
692 }
693 }
694 }
695
696 if( wins.get_count() < MAX_WIN ) {
697
698 if (!wins.empty()) {
699 // mark old dirty
700 const scr_size size = wins.back().gui->get_windowsize();
701 mark_rect_dirty_wc( wins.back().pos.x - 1, wins.back().pos.y - 1, wins.back().pos.x + size.w + 2, wins.back().pos.y + size.h + 2 + D_TITLEBAR_HEIGHT ); // -1, +2 for env_t::window_frame_active
702 }
703
704 wins.append( simwin_t() );
705 simwin_t& win = wins.back();
706
707 sint16 const menu_height = env_t::iconsize.h;
708
709 // (Mathew Hounsell) Make Sure Closes Aren't Forgotten.
710 // Must Reset as the entries and thus flags are reused
711 win.flags.close = true;
712 win.flags.title = gui->has_title();
713 win.flags.help = ( gui->get_help_filename() != NULL );
714 win.flags.prev = gui->has_prev();
715 win.flags.next = gui->has_next();
716 win.flags.size = gui->has_min_sizer();
717 win.flags.sticky = gui->has_sticky();
718 win.gui = gui;
719
720 // take care of time delete windows ...
721 win.wt = wt & w_time_delete ? w_info : wt;
722 win.dauer = (wt&w_time_delete) ? MESG_WAIT : -1;
723 win.magic_number = magic;
724 win.gadget_state = 0;
725 win.rollup = false;
726 win.sticky = false;
727 win.dirty = true;
728
729 // Hajo: Notify window to be shown
730 assert(gui);
731 event_t ev;
732
733 ev.ev_class = INFOWIN;
734 ev.ev_code = WIN_OPEN;
735 ev.mx = 0;
736 ev.my = 0;
737 ev.cx = 0;
738 ev.cy = 0;
739 ev.button_state = 0;
740
741 void *old = inside_event_handling;
742 inside_event_handling = gui;
743 gui->infowin_event(&ev);
744 inside_event_handling = old;
745
746 // restore windowsize
747 scr_size stored = get_stored_windowsize(&win);
748
749 if (stored == scr_size()) {
750 // not stored, take current
751 stored = gui->get_windowsize();
752 }
753 // use default width
754 stored.clip_lefttop(scr_size(D_DEFAULT_WIDTH, D_DEFAULT_HEIGHT));
755 // clip to display size
756 stored.clip_rightbottom( scr_size(display_get_width(), display_get_height() - env_t::iconsize.h - win_get_statusbar_height() ) );
757
758 if (stored != gui->get_windowsize()) {
759 // send tailored resize event
760 scr_size delta = stored - gui->get_windowsize();
761 event_t wev;
762 wev.ev_class = WINDOW_RESIZE;
763 wev.ev_code = 0;
764 wev.mx = delta.w;
765 wev.my = delta.h;
766
767 inside_event_handling = gui;
768 gui->infowin_event(&wev);
769 inside_event_handling = old;
770 }
771
772 scr_size size = gui->get_windowsize();
773
774 if(x == -1) {
775 // try to keep the toolbar below all other toolbars
776 y = menu_height;
777 if(wt & w_no_overlap) {
778 for( uint32 i=0; i<wins.get_count()-1; i++ ) {
779 if(wins[i].wt & w_no_overlap) {
780 if(wins[i].pos.y>=y) {
781 sint16 lower_y = wins[i].pos.y + wins[i].gui->get_windowsize().h;
782 if(lower_y >= y) {
783 y = lower_y;
784 }
785 }
786 }
787 }
788 // right aligned
789 // x = max( 0, display_get_width()-size.w );
790 // but we go for left
791 x = 0;
792 y = min( y, display_get_height()-size.h );
793 }
794 else {
795 x = min(get_mouse_x() - size.w/2, display_get_width()-size.w);
796 y = min(get_mouse_y() - size.h-env_t::iconsize.h, display_get_height()-size.h);
797 }
798 }
799 if(x<0) {
800 x = 0;
801 }
802 if(y<menu_height) {
803 y = menu_height;
804 }
805 win.pos = scr_coord(x,y);
806 win.dirty = true;
807 return wins.get_count();
808 }
809 else {
810 if( !( wt & w_do_not_delete ) ) {
811 delete gui;
812 }
813 return -1;
814 }
815 }
816
817
818 /* sometimes a window cannot destroyed while it is still handled;
819 * in those cases it will added to kill list and it is only destructed
820 * by this function
821 */
process_kill_list()822 static void process_kill_list()
823 {
824 FOR(vector_tpl<simwin_t>, & i, kill_list) {
825 wins.remove(i);
826 destroy_framed_win(&i);
827 }
828 kill_list.clear();
829 }
830
831
832 /**
833 * Destroy a framed window
834 * @author Hj. Malthaner
835 */
destroy_framed_win(simwin_t * wins)836 static bool destroy_framed_win(simwin_t *wins)
837 {
838 bool r = true;
839
840 // mark dirty
841 const scr_size size = wins->gui->get_windowsize();
842 mark_rect_dirty_wc( wins->pos.x - 1, wins->pos.y - 1, wins->pos.x + size.w + 2, wins->pos.y + size.h + 2 ); // -1, +2 for env_t::window_frame_active
843
844 // save windowsize for later
845 save_windowsize(wins);
846
847 gui_frame_t* gui = wins->gui; // save pointer to gui window: might be modified in event handling, or could be modified if wins points to value in kill_list and kill_list is modified! nasty surprise
848 if( gui ) {
849 event_t ev;
850
851 ev.ev_class = INFOWIN;
852 ev.ev_code = WIN_CLOSE;
853 ev.mx = 0;
854 ev.my = 0;
855 ev.cx = 0;
856 ev.cy = 0;
857 ev.button_state = 0;
858
859 void *old = inside_event_handling;
860 inside_event_handling = gui;
861 wins->gui->infowin_event(&ev);
862 inside_event_handling = old;
863 }
864
865 if( (wins->wt&w_do_not_delete) == 0 ) {
866 if( wins->gui == gui ) {
867 // remove from kill list first, otherwise delete will be called again on that window
868 for( uint32 j = 0; j < kill_list.get_count(); j++ ) {
869 if( kill_list[j].gui == gui ) {
870 kill_list.remove_at(j);
871 break;
872 }
873 }
874 delete gui;
875 }
876 else {
877 // wins likely modified during event handling. Assume this was just a schedule window destroying itself, and top_win() therefore modifying wins.
878 // return false to signal destroy_all_win() that wins was already modified.
879 r = false;
880 }
881 }
882 // set dirty flag to refill background
883 if( wl ) {
884 wl->set_background_dirty();
885 }
886 return r;
887 }
888
889
destroy_win(const ptrdiff_t magic)890 bool destroy_win(const ptrdiff_t magic)
891 {
892 const gui_frame_t *gui = win_get_magic(magic);
893 if(gui) {
894 return destroy_win( gui );
895 }
896 return false;
897 }
898
899
destroy_win(const gui_frame_t * gui)900 bool destroy_win(const gui_frame_t *gui)
901 {
902 if(wins.get_count() > 1 && wins.back().gui == gui) {
903 // destroy topped window, top the next window before removing
904 // do it here as top_win manipulates the win vector
905 top_win(wins.get_count() - 2, true);
906 }
907
908 for( uint i=0; i<wins.get_count(); i++ ) {
909 if(wins[i].gui == gui) {
910 if(inside_event_handling==wins[i].gui) {
911 kill_list.append_unique(wins[i]);
912 }
913 else {
914 simwin_t win = wins[i];
915 wins.remove_at(i);
916 if( win.magic_number < magic_max ) {
917 // save last pos
918 if( scr_coord *k = old_win_pos.access(win.magic_number) ) {
919 *k = win.pos;
920 }
921 else {
922 old_win_pos.put( win.magic_number, win.pos );
923 }
924 }
925 destroy_framed_win(&win);
926 }
927 return true;
928 break;
929 }
930 }
931 return false;
932 }
933
934
destroy_all_win(bool destroy_sticky)935 void destroy_all_win(bool destroy_sticky)
936 {
937 for( sint32 curWin = 0; curWin < (sint32)wins.get_count(); curWin++ ) {
938 if( destroy_sticky || !wins[curWin].sticky ) {
939 if( inside_event_handling == wins[curWin].gui ) {
940 // only add this, if not already added
941 kill_list.append_unique(wins[curWin]);
942 // compact the window list
943 wins.remove_at(curWin);
944 curWin--;
945 }
946 else {
947 if( destroy_framed_win(&wins[curWin]) ) {
948 // compact the window list
949 wins.remove_at(curWin);
950 curWin--;
951 }
952 // else wins was already modified - assume by the schedule window closing itself during event handling
953 }
954 }
955 }
956 }
957
958
top_win(int win,bool keep_state)959 int top_win(int win, bool keep_state )
960 {
961 if( (uint32)win==wins.get_count()-1 ) {
962 return win;
963 } // already topped
964
965 // mark old dirty
966 scr_size size = wins.back().gui->get_windowsize();
967 mark_rect_dirty_wc( wins.back().pos.x - 1, wins.back().pos.y - 1, wins.back().pos.x + size.w + 2, wins.back().pos.y + size.h + 2 ); // -1, +2 for env_t::window_frame_active
968 wins.back().dirty = true;
969
970 simwin_t tmp = wins[win];
971 wins.remove_at(win);
972 if( !keep_state ) {
973 tmp.rollup = false; // make visible when topping
974 }
975 wins.append(tmp);
976
977 // mark new dirty
978 size = wins.back().gui->get_windowsize();
979 mark_rect_dirty_wc( wins.back().pos.x - 1, wins.back().pos.y - 1, wins.back().pos.x + size.w + 2, wins.back().pos.y + size.h + 2 ); // -1, +2 for env_t::window_frame_active
980
981 event_t ev;
982
983 ev.ev_class = INFOWIN;
984 ev.ev_code = WIN_TOP;
985 ev.mx = 0;
986 ev.my = 0;
987 ev.cx = 0;
988 ev.cy = 0;
989 ev.button_state = 0;
990
991 void *old = inside_event_handling;
992 inside_event_handling = tmp.gui;
993 tmp.gui->infowin_event(&ev);
994 inside_event_handling = old;
995
996 return wins.get_count()-1;
997 }
998
999
display_win(int win)1000 void display_win(int win)
1001 {
1002 // ok, now process it
1003 gui_frame_t *comp = wins[win].gui;
1004 scr_size size = comp->get_windowsize();
1005 scr_coord pos = wins[win].pos;
1006 FLAGGED_PIXVAL title_color = (comp->get_titlecolor()&0xFFFF);
1007 FLAGGED_PIXVAL text_color = env_t::front_window_text_color;
1008 if( (unsigned)win!=wins.get_count()-1 ) {
1009 // not top => darker
1010 title_color = display_blend_colors(title_color, color_idx_to_rgb(COL_BLACK), env_t::bottom_window_darkness);
1011 text_color = env_t::bottom_window_text_color;
1012 }
1013 bool need_dragger = comp->get_resizemode() != gui_frame_t::no_resize;
1014
1015 // %HACK (Mathew Hounsell) So draw will know if gadget is needed.
1016 wins[win].flags.help = ( comp->get_help_filename() != NULL );
1017 if( wins[win].flags.title ) {
1018 win_draw_window_title(wins[win].pos,
1019 size,
1020 title_color,
1021 comp->get_name(),
1022 text_color,
1023 comp->get_weltpos(false),
1024 wins[win].gadget_state,
1025 wins[win].sticky,
1026 comp->is_weltpos(),
1027 wins[win].flags );
1028 }
1029 if( wins[win].dirty ) {
1030 // not sure this is still a useful call
1031 mark_rect_dirty_wc( wins[win].pos.x, wins[win].pos.y, wins[win].pos.x+size.w+1, wins[win].pos.y+2 );
1032 wins[win].dirty = false;
1033 }
1034 // mark top window, if requested
1035 if(env_t::window_frame_active && (unsigned)win==wins.get_count()-1) {
1036 const int y_off = wins[win].flags.title ? 0 : D_TITLEBAR_HEIGHT;
1037 if(!wins[win].rollup) {
1038 display_ddd_box_clip_rgb( wins[win].pos.x-1, wins[win].pos.y-1 + y_off, size.w+2, size.h+2 - y_off, title_color, title_color); //, wins[win].dirty | wins[win].gui->is_dirty() );
1039 }
1040 else {
1041 display_ddd_box_clip_rgb( wins[win].pos.x-1, wins[win].pos.y-1 + y_off, size.w+2, D_TITLEBAR_HEIGHT + 2 - y_off, title_color, title_color); //, wins[win].dirty | wins[win].gui->is_dirty() );
1042 }
1043 }
1044 if(!wins[win].rollup) {
1045 comp->draw(wins[win].pos, size);
1046
1047 // draw dragger
1048 if(need_dragger) {
1049 win_draw_window_dragger( pos, size);
1050 }
1051 }
1052 }
1053
1054
display_all_win()1055 void display_all_win()
1056 {
1057 // first: empty kill list
1058 process_kill_list();
1059
1060 // check which window can set tooltip
1061 const sint16 x = get_mouse_x();
1062 const sint16 y = get_mouse_y();
1063 tooltip_element = NULL;
1064 for( uint32 i = wins.get_count(); i-- != 0; ) {
1065 if( (!wins[i].rollup && wins[i].gui->is_hit(x-wins[i].pos.x,y-wins[i].pos.y)) ||
1066 (wins[i].rollup && x>=wins[i].pos.x && x<wins[i].pos.x+wins[i].gui->get_windowsize().w && y>=wins[i].pos.y && y<wins[i].pos.y+D_TITLEBAR_HEIGHT)
1067 ) {
1068 // tooltips are only allowed for this window
1069 tooltip_element = wins[i].gui;
1070 break;
1071 }
1072 }
1073
1074 // then display windows
1075 for( uint i=0; i<wins.get_count(); i++ ) {
1076 void *old_gui = inside_event_handling;
1077 inside_event_handling = wins[i].gui;
1078 display_win(i);
1079 inside_event_handling = old_gui;
1080 }
1081 }
1082
1083
win_rotate90(sint16 new_ysize)1084 void win_rotate90( sint16 new_ysize )
1085 {
1086 FOR(vector_tpl<simwin_t>, const& i, wins) {
1087 i.gui->map_rotate90(new_ysize);
1088 }
1089 }
1090
1091
remove_old_win()1092 static void remove_old_win()
1093 {
1094 // Destroy (close) old window when life time expire
1095 for( int i=wins.get_count()-1; i>=0; i=min(i,(int)wins.get_count())-1 ) {
1096 if(wins[i].dauer > 0) {
1097 wins[i].dauer --;
1098 if(wins[i].dauer == 0) {
1099 destroy_win( wins[i].gui );
1100 }
1101 }
1102 }
1103 }
1104
1105
snap_check_distance(scr_coord_val * r,const scr_coord_val a,const scr_coord_val b)1106 static inline void snap_check_distance( scr_coord_val *r, const scr_coord_val a, const scr_coord_val b )
1107 {
1108 if( abs(a-b)<=env_t::window_snap_distance ) {
1109 *r = a;
1110 }
1111 }
1112
1113
snap_check_win(const int win,scr_coord * r,const scr_coord from_pos,const scr_coord from_size,const scr_coord to_pos,const scr_coord to_size)1114 void snap_check_win( const int win, scr_coord *r, const scr_coord from_pos, const scr_coord from_size, const scr_coord to_pos, const scr_coord to_size )
1115 {
1116 bool resize;
1117 if( from_size==to_size && from_pos!=to_pos ) { // check if we're moving
1118 resize = false;
1119 }
1120 else if( from_size!=to_size && from_pos==from_pos ) { // or resizing the window
1121 resize = true;
1122 }
1123 else {
1124 return; // or nothing to do.
1125 }
1126
1127 const int wins_count = wins.get_count();
1128
1129 for( int i=0; i<=wins_count; i++ ) {
1130 if( i==win ) {
1131 // Don't snap to self
1132 continue;
1133 }
1134
1135 scr_coord other_pos;
1136 scr_coord other_size;
1137
1138 if( i==wins_count ) {
1139 // Allow snap to screen edge
1140 other_pos.x = 0;
1141 other_pos.y = env_t::iconsize.h;
1142 other_size.x = display_get_width();
1143 other_size.y = display_get_height()-win_get_statusbar_height()-other_pos.y;
1144 if( show_ticker ) {
1145 other_size.y -= TICKER_HEIGHT;
1146 }
1147 }
1148 else {
1149 // Snap to other window
1150 other_size = wins[i].gui->get_windowsize();
1151 other_pos = wins[i].pos;
1152 if( wins[i].rollup ) {
1153 other_size.y = D_TITLEBAR_HEIGHT;
1154 }
1155 }
1156
1157 // my bottom below other top and my top above other bottom ---- in same vertical band
1158 if( from_pos.y+from_size.y>=other_pos.y && from_pos.y<=other_pos.y+other_size.y ) {
1159 if( resize ) {
1160 // other right side and my new right side within snap
1161 snap_check_distance( &r->x, other_pos.x+other_size.x-from_pos.x, to_size.x ); // snap right - align right sides
1162 }
1163 else {
1164 // other right side and my new right side within snap
1165 snap_check_distance( &r->x, other_pos.x+other_size.x-from_size.x, to_pos.x ); // snap right - align right sides
1166
1167 // other left side and my new left side within snap
1168 snap_check_distance( &r->x, other_pos.x, to_pos.x ); // snap left - align left sides
1169 }
1170 }
1171
1172 // my new bottom below other top and my new top above other bottom ---- in same vertical band
1173 if( resize ) {
1174 if( from_pos.y+to_size.y>other_pos.y && from_pos.y<other_pos.y+other_size.y ) {
1175 // other left side and my new right side within snap
1176 snap_check_distance( &r->x, other_pos.x-from_pos.x, to_size.x ); // snap right - align my right to other left
1177 }
1178 }
1179 else {
1180 if( to_pos.y+from_size.y>other_pos.y && to_pos.y<other_pos.y+other_size.y ) {
1181 // other left side and my new right side within snap
1182 snap_check_distance( &r->x, other_pos.x-from_size.x, to_pos.x ); // snap right - align my right to other left
1183
1184 // other right side and my new left within snap
1185 snap_check_distance( &r->x, other_pos.x+other_size.x, to_pos.x ); // snap left - align my left to other right
1186 }
1187 }
1188
1189 // my right side right of other left side and my left side left of other right side ---- in same horizontal band
1190 if( from_pos.x+from_size.x>=other_pos.x && from_pos.x<=other_pos.x+other_size.x ) {
1191 if( resize ) {
1192 // other bottom and my new bottom within snap
1193 snap_check_distance( &r->y, other_pos.y+other_size.y-from_pos.y, to_size.y ); // snap down - align bottoms
1194 }
1195 else {
1196 // other bottom and my new bottom within snap
1197 snap_check_distance( &r->y, other_pos.y+other_size.y-from_size.y, to_pos.y ); // snap down - align bottoms
1198
1199 // other top and my new top within snap
1200 snap_check_distance( &r->y, other_pos.y, to_pos.y ); // snap up - align tops
1201 }
1202 }
1203
1204 // my new right side right of other left side and my new left side left of other right side ---- in same horizontal band
1205 if ( resize ) {
1206 if( from_pos.x+to_size.x>other_pos.x && from_pos.x<other_pos.x+other_size.x ) {
1207 // other top and my new bottom within snap
1208 snap_check_distance( &r->y, other_pos.y-from_pos.y, to_size.y ); // snap down - align my bottom to other top
1209 }
1210 }
1211 else {
1212 if( to_pos.x+from_size.x>other_pos.x && to_pos.x<other_pos.x+other_size.x ) {
1213 // other top and my new bottom within snap
1214 snap_check_distance( &r->y, other_pos.y-from_size.y, to_pos.y ); // snap down - align my bottom to other top
1215
1216 // other bottom and my new top within snap
1217 snap_check_distance( &r->y, other_pos.y+other_size.y, to_pos.y ); // snap up - align my top to other bottom
1218 }
1219 }
1220 }
1221 }
1222
1223
move_win(int win,event_t * ev)1224 void move_win(int win, event_t *ev)
1225 {
1226 const scr_coord mouse_from( ev->cx, ev->cy );
1227 const scr_coord mouse_to( ev->mx, ev->my );
1228
1229 const scr_coord from_pos = wins[win].pos;
1230 scr_coord from_size = scr_coord(wins[win].gui->get_windowsize().w,wins[win].gui->get_windowsize().h);
1231 if( wins[win].rollup ) {
1232 from_size.y = D_TITLEBAR_HEIGHT + 2;
1233 }
1234
1235 scr_coord to_pos = wins[win].pos+(mouse_to-mouse_from);
1236 const scr_coord to_size = from_size;
1237
1238 if( env_t::window_snap_distance>0 ) {
1239 snap_check_win( win, &to_pos, from_pos, from_size, to_pos, to_size );
1240 }
1241
1242 // CLIP(wert,min,max)
1243 to_pos.x = CLIP( to_pos.x, 8-to_size.x, display_get_width()-16 );
1244 to_pos.y = CLIP( to_pos.y, env_t::iconsize.h, display_get_height() - D_TITLEBAR_HEIGHT - win_get_statusbar_height() - TICKER_HEIGHT);
1245
1246 // delta is actual window movement.
1247 const scr_coord delta = to_pos - from_pos;
1248
1249 wins[win].pos += delta;
1250 // need to mark all of old and new positions dirty. -1, +2 for env_t::window_frame_active
1251 mark_rect_dirty_wc( from_pos.x - 1, from_pos.y - 1, from_pos.x + from_size.x + 2, from_pos.y + from_size.y + 2 );
1252 wins[win].dirty = true;
1253 // set dirty flag to refill background
1254 if(wl) {
1255 wl->set_background_dirty();
1256 }
1257
1258 change_drag_start( delta.x, delta.y );
1259 }
1260
1261
resize_win(int win,event_t * ev)1262 void resize_win(int win, event_t *ev)
1263 {
1264 event_t wev = *ev;
1265 wev.ev_class = WINDOW_RESIZE;
1266 wev.ev_code = 0;
1267
1268 const scr_coord mouse_from( wev.cx, wev.cy );
1269 const scr_coord mouse_to( wev.mx, wev.my );
1270
1271 const scr_coord from_pos = wins[win].pos;
1272 const scr_coord from_size = scr_coord(wins[win].gui->get_windowsize().w,wins[win].gui->get_windowsize().h);
1273
1274 const scr_coord to_pos = from_pos;
1275 scr_coord to_size = from_size+(mouse_to-mouse_from);
1276
1277 if( env_t::window_snap_distance>0 ) {
1278 snap_check_win( win, &to_size, from_pos, from_size, to_pos, to_size );
1279 }
1280
1281 // since we may be smaller afterwards
1282 mark_rect_dirty_wc( from_pos.x - 1, from_pos.y - 1, from_pos.x + from_size.x + 2, from_pos.y + from_size.y + 2 ); // -1, +2 for env_t::window_frame_active
1283 // set dirty flag to refill background
1284 if(wl) {
1285 wl->set_background_dirty();
1286 }
1287
1288 // adjust event mouse scr_coord per snap
1289 wev.mx = wev.cx + to_size.x - from_size.x;
1290 wev.my = wev.cy + to_size.y - from_size.y;
1291
1292 wins[win].gui->infowin_event( &wev );
1293 }
1294
1295
1296 // returns true, if gui is a open window handle
win_is_open(gui_frame_t * gui)1297 bool win_is_open(gui_frame_t *gui)
1298 {
1299 FOR(vector_tpl<simwin_t>, const& i, wins) {
1300 if (i.gui == gui) {
1301 FOR(vector_tpl<simwin_t>, const& j, kill_list) {
1302 if (j.gui == gui) {
1303 return false;
1304 }
1305 }
1306 return true;
1307 }
1308 }
1309 return false;
1310 }
1311
1312
win_get_pos(gui_frame_t const * const gui)1313 scr_coord const& win_get_pos(gui_frame_t const* const gui)
1314 {
1315 for( uint32 i = wins.get_count(); i-- != 0; ) {
1316 if( wins[i].gui == gui ) {
1317 return wins[i].pos;
1318 }
1319 }
1320 static scr_coord const bad(-1, -1);
1321 return bad;
1322 }
1323
1324
win_set_pos(gui_frame_t * gui,int x,int y)1325 void win_set_pos(gui_frame_t *gui, int x, int y)
1326 {
1327 for( uint32 i = wins.get_count(); i-- != 0; ) {
1328 if( wins[i].gui == gui ) {
1329 wins[i].pos.x = x;
1330 wins[i].pos.y = y;
1331 wins[i].dirty = true;
1332 return;
1333 }
1334 }
1335 }
1336
1337
1338 /* main window event handler
1339 * renovated may 2005 by prissi to take care of irregularly shaped windows
1340 * also remove some unnecessary calls
1341 */
check_pos_win(event_t * ev)1342 bool check_pos_win(event_t *ev)
1343 {
1344 static int is_resizing = -1;
1345 static int is_moving = -1;
1346
1347 bool swallowed = false;
1348
1349 const int x = ev->ev_class==EVENT_MOVE ? ev->mx : ev->cx;
1350 const int y = ev->ev_class==EVENT_MOVE ? ev->my : ev->cy;
1351
1352 // for the moment, no none events
1353 if (ev->ev_class == EVENT_NONE) {
1354 process_kill_list();
1355 return false;
1356 }
1357
1358 // we stop resizing once the user releases the button
1359 if( (is_resizing>=0 || is_moving>=0) && (IS_LEFTRELEASE(ev) || (ev->button_state&1)==0) ) {
1360 is_resizing = -1;
1361 is_moving = -1;
1362 if( IS_LEFTRELEASE(ev) ) {
1363 // Knightly : should not proceed, otherwise the left release event will be fed to other components;
1364 // return true (i.e. event swallowed) to prevent propagation back to the main view
1365 return true;
1366 }
1367 }
1368
1369 // Knightly : disable any active tooltip upon mouse click by forcing expiration of tooltip duration
1370 if( ev->ev_class==EVENT_CLICK ) {
1371 tooltip_register_time = 0;
1372 }
1373
1374 // click in main menu?
1375 if (!tool_t::toolbar_tool.empty() &&
1376 tool_t::toolbar_tool[0]->get_tool_selector() &&
1377 env_t::iconsize.h > y &&
1378 ev->ev_class != EVENT_KEYBOARD) {
1379 event_t wev = *ev;
1380 inside_event_handling = tool_t::toolbar_tool[0];
1381 tool_t::toolbar_tool[0]->get_tool_selector()->infowin_event( &wev );
1382 inside_event_handling = NULL;
1383 // swallow event
1384 return true;
1385 }
1386
1387 // cursor event only go to top window (but not if rolled up)
1388 if( (ev->ev_class == EVENT_KEYBOARD || ev->ev_class == EVENT_STRING) && !wins.empty() ) {
1389 simwin_t &win = wins.back();
1390 if( !win.rollup ) {
1391 inside_event_handling = win.gui;
1392 swallowed = win.gui->infowin_event(ev);
1393 }
1394 inside_event_handling = NULL;
1395 process_kill_list();
1396 return swallowed;
1397 }
1398
1399 // just move top window until button release
1400 if( is_moving>=0 && (unsigned)is_moving<wins.get_count() && (IS_LEFTDRAG(ev) || IS_LEFTREPEAT(ev)) ) {
1401 move_win( is_moving, ev );
1402 return true;
1403 }
1404
1405 // just resize window until button release
1406 if( is_resizing>=0 && (unsigned)is_resizing<wins.get_count() && (IS_LEFTDRAG(ev) || IS_LEFTREPEAT(ev)) ) {
1407 resize_win( is_resizing, ev );
1408 return true;
1409 }
1410
1411 // swallow all other events in the infobar
1412 if( !(ev->ev_class == EVENT_KEYBOARD || ev->ev_class == EVENT_STRING) && y > display_get_height()- win_get_statusbar_height() ) {
1413 // swallow event
1414 return true;
1415 }
1416
1417 // swallow all other events in ticker (if there)
1418 if( !(ev->ev_class == EVENT_KEYBOARD || ev->ev_class == EVENT_STRING) && show_ticker && y > display_get_height()- win_get_statusbar_height() - TICKER_HEIGHT ) {
1419 if( IS_LEFTCLICK(ev) ) {
1420 // goto infowin koordinate, if ticker is active
1421 koord p = ticker::get_welt_pos();
1422 if(wl->is_within_limits(p)) {
1423 wl->get_viewport()->change_world_position(koord3d(p,wl->min_hgt(p)));
1424 }
1425 }
1426 // swallow event
1427 return true;
1428 }
1429
1430 // handle all the other events
1431 for( int i=wins.get_count()-1; i>=0 && !swallowed; i=min(i,(int)wins.get_count())-1 ) {
1432
1433 if( wins[i].gui->is_hit( x-wins[i].pos.x, y-wins[i].pos.y ) ) {
1434
1435 // all events in window are swallowed
1436 swallowed = true;
1437
1438 inside_event_handling = wins[i].gui;
1439
1440 // Top window first
1441 if( (int)wins.get_count()-1>i && IS_LEFTCLICK(ev) && (!wins[i].rollup || ev->cy<wins[i].pos.y+D_TITLEBAR_HEIGHT) ) {
1442 i = top_win(i,false);
1443 }
1444
1445 // Hajo: if within title bar && window needs decoration
1446 // Max Kielland: Use title height
1447 if( y<wins[i].pos.y+D_TITLEBAR_HEIGHT && wins[i].flags.title ) {
1448 // no more moving
1449 is_moving = -1;
1450 wins[i].dirty = true;
1451
1452 // %HACK (Mathew Hounsell) So decode will know if gadget is needed.
1453 wins[i].flags.help = ( wins[i].gui->get_help_filename() != NULL );
1454
1455 // Where Was It ?
1456 sint8 code = decode_gadget_boxes( ( & wins[i].flags ), wins[i].pos.x + (REVERSE_GADGETS?0:wins[i].gui->get_windowsize().w-D_GADGET_WIDTH), x );
1457
1458 if( code < SKIN_GADGET_COUNT ) {
1459 if( IS_LEFTCLICK(ev) ) {
1460 wins[i].gadget_state |= (1 << code);
1461 }
1462 else if( IS_LEFTRELEASE(ev) ) {
1463 wins[i].gadget_state &= ~(1 << code);
1464 if( ev->my >= wins[i].pos.y && ev->my < wins[i].pos.y+D_TITLEBAR_HEIGHT && decode_gadget_boxes( ( & wins[i].flags ), wins[i].pos.x + (REVERSE_GADGETS?0:wins[i].gui->get_windowsize().w-D_GADGET_WIDTH), ev->mx )==code ) {
1465 // do whatever needs to be done
1466 switch( code ) {
1467 case SKIN_GADGET_CLOSE :
1468 destroy_win(wins[i].gui);
1469 break;
1470 case SKIN_GADGET_MINIMIZE: // (Mathew Hounsell)
1471 ev->ev_class = WINDOW_MAKE_MIN_SIZE;
1472 ev->ev_code = 0;
1473 wins[i].gui->infowin_event( ev );
1474 break;
1475 case SKIN_GADGET_HELP :
1476 help_frame_t::open_help_on( wins[i].gui->get_help_filename() );
1477 break;
1478 case SKIN_BUTTON_PREVIOUS:
1479 ev->ev_class = WINDOW_CHOOSE_NEXT;
1480 ev->ev_code = PREV_WINDOW; // backward
1481 wins[i].gui->infowin_event( ev );
1482 break;
1483 case SKIN_BUTTON_NEXT:
1484 ev->ev_class = WINDOW_CHOOSE_NEXT;
1485 ev->ev_code = NEXT_WINDOW; // forward
1486 wins[i].gui->infowin_event( ev );
1487 break;
1488 case SKIN_GADGET_GOTOPOS:
1489 { // change position on map (or follow)
1490 koord3d k = wins[i].gui->get_weltpos(true);
1491 if( k!=koord3d::invalid ) {
1492 wl->get_viewport()->change_world_position( k );
1493 }
1494 }
1495 break;
1496 case SKIN_GADGET_NOTPINNED:
1497 wins[i].sticky = !wins[i].sticky;
1498 break;
1499 }
1500 }
1501 }
1502 }
1503 else {
1504 // Somewhere on the titlebar
1505 if (IS_LEFTDRAG(ev)) {
1506 i = top_win(i,false);
1507 move_win(i, ev);
1508 is_moving = i;
1509 }
1510 if(IS_RIGHTCLICK(ev)) {
1511 wins[i].rollup ^= 1;
1512 gui_frame_t *gui = wins[i].gui;
1513 scr_size size = gui->get_windowsize();
1514 mark_rect_dirty_wc( wins[i].pos.x, wins[i].pos.y, wins[i].pos.x+size.w, wins[i].pos.y+size.h );
1515 if( wins[i].rollup ) {
1516 wl->set_background_dirty();
1517 }
1518 }
1519 }
1520
1521 // It has been handled so stop checking.
1522 break;
1523
1524 }
1525 else {
1526 if(!wins[i].rollup) {
1527 // click in Window / Resize?
1528 //11-May-02 markus weber added
1529
1530 scr_size size = wins[i].gui->get_windowsize();
1531
1532 // resizer hit ?
1533 const bool canresize = is_resizing>=0 ||
1534 (ev->cx > wins[i].pos.x + size.w - dragger_size &&
1535 ev->cy > wins[i].pos.y + size.h - dragger_size);
1536
1537 if((IS_LEFTCLICK(ev) || IS_LEFTDRAG(ev) || IS_LEFTREPEAT(ev)) && canresize && wins[i].gui->get_resizemode()!=gui_frame_t::no_resize) {
1538 resize_win( i, ev );
1539 is_resizing = i;
1540 }
1541 else {
1542 is_resizing = -1;
1543 // click in Window
1544 event_t wev = *ev;
1545 translate_event(&wev, -wins[i].pos.x, -wins[i].pos.y);
1546 wins[i].gui->infowin_event( &wev );
1547 }
1548 }
1549 else {
1550 swallowed = false;
1551 }
1552 }
1553 inside_event_handling = NULL;
1554 }
1555 }
1556
1557 inside_event_handling = NULL;
1558 process_kill_list();
1559
1560 return swallowed;
1561 }
1562
1563
win_poll_event(event_t * const ev)1564 void win_poll_event(event_t* const ev)
1565 {
1566 display_poll_event(ev);
1567 // main window resized
1568 if( ev->ev_class==EVENT_SYSTEM && ev->ev_code==SYSTEM_RESIZE ) {
1569 // main window resized
1570 simgraph_resize( ev->size_x, ev->size_y );
1571 ticker::redraw();
1572 wl->set_dirty();
1573 wl->get_viewport()->metrics_updated();
1574 ev->ev_class = EVENT_NONE;
1575 }
1576 // save and reload all windows (currently only used when a new theme is applied)
1577 if( ev->ev_class==EVENT_SYSTEM && ev->ev_code==SYSTEM_RELOAD_WINDOWS ) {
1578 dr_chdir( env_t::user_dir );
1579 loadsave_t dlg;
1580 if( dlg.wr_open( "dlgpos.xml", loadsave_t::xml_zipped, "temp", SERVER_SAVEGAME_VER_NR ) ) {
1581 // save all
1582 rdwr_all_win( &dlg );
1583 dlg.close();
1584 destroy_all_win( true );
1585 if( dlg.rd_open( "dlgpos.xml" ) ) {
1586 // and reload them ...
1587 rdwr_all_win( &dlg );
1588 }
1589 }
1590 wl->set_dirty();
1591 ev->ev_class = EVENT_NONE;
1592 ticker::redraw();
1593 }
1594 if( ev->ev_class==EVENT_SYSTEM && ev->ev_code==SYSTEM_THEME_CHANGED ) {
1595 // called when font is changed
1596 ev->mx = ev->my = ev->cx = ev->cy = 0;
1597 FOR(vector_tpl<simwin_t>, const& i, wins) {
1598 i.gui->infowin_event(ev);
1599 }
1600 ev->ev_class = EVENT_NONE;
1601 ticker::redraw();
1602 }
1603 }
1604
1605
win_get_statusbar_height()1606 uint16 win_get_statusbar_height()
1607 {
1608 return max(LINESPACE + 2, 15);
1609 }
1610
1611 // finally updates the display
win_display_flush(double konto)1612 void win_display_flush(double konto)
1613 {
1614 const sint16 disp_width = display_get_width();
1615 const sint16 disp_height = display_get_height();
1616 const sint16 menu_height = env_t::iconsize.h;
1617
1618 // display main menu
1619 tool_selector_t *main_menu = tool_t::toolbar_tool[0]->get_tool_selector();
1620 display_set_clip_wh( 0, 0, disp_width, menu_height+1 );
1621 if( skinverwaltung_t::toolbar_background && skinverwaltung_t::toolbar_background->get_image_id(0) != IMG_EMPTY ) {
1622 const image_id back_img = skinverwaltung_t::toolbar_background->get_image_id(0);
1623 scr_coord_val w = env_t::iconsize.w;
1624 scr_rect row = scr_rect( 0, 0, disp_width, menu_height );
1625 display_fit_img_to_width( back_img, w );
1626 // tile it wide
1627 while( w <= row.w ) {
1628 display_color_img( back_img, row.x, row.y, 0, false, true );
1629 row.x += w;
1630 row.w -= w;
1631 }
1632 // for the rest we have to clip the rectangle
1633 if( row.w > 0 ) {
1634 clip_dimension const cl = display_get_clip_wh();
1635 display_set_clip_wh( cl.x, cl.y, max(0, min(row.get_right(), cl.xx) - cl.x), cl.h );
1636 display_color_img( back_img, row.x, row.y, 0, false, true );
1637 display_set_clip_wh( cl.x, cl.y, cl.w, cl.h );
1638 }
1639 }
1640 else {
1641 display_fillbox_wh_rgb( 0, 0, disp_width, menu_height, color_idx_to_rgb(MN_GREY2), false );
1642 }
1643 // .. extra logic to enable tooltips
1644 tooltip_element = menu_height > get_mouse_y() ? main_menu : NULL;
1645 void *old_inside_event_handling = inside_event_handling;
1646 inside_event_handling = main_menu;
1647 main_menu->draw( scr_coord(0,-D_TITLEBAR_HEIGHT), scr_size(disp_width,menu_height) );
1648 inside_event_handling = old_inside_event_handling;
1649
1650 display_set_clip_wh( 0, menu_height, disp_width, disp_height-menu_height+1 );
1651
1652 show_ticker = false;
1653 ticker::draw();
1654 if (ticker::empty()) {
1655 // set dirty background for removing ticker
1656 if(wl) {
1657 wl->set_background_dirty();
1658 }
1659 }
1660 else {
1661 show_ticker = true;
1662 // need to adapt tooltip_y coordinates
1663 tooltip_ypos = min(tooltip_ypos, disp_height-15-10-16);
1664 }
1665
1666 if( skinverwaltung_t::compass_iso && env_t::compass_screen_position ) {
1667 display_img_aligned( skinverwaltung_t::compass_iso->get_image_id( wl->get_settings().get_rotation() ), scr_rect(4,menu_height+4,disp_width-2*4,disp_height-menu_height-15-2*4-(TICKER_HEIGHT)*show_ticker), env_t::compass_screen_position, false );
1668 }
1669
1670 {
1671 // clip windows to avoid drawing into menu, ticker, or status-bar
1672 PUSH_CLIP(0, menu_height, display_get_width(), display_get_height() - menu_height - (TICKER_HEIGHT)*show_ticker - win_get_statusbar_height() );
1673
1674 display_all_win();
1675 remove_old_win();
1676
1677 if(env_t::show_tooltips) {
1678 // Hajo: check if there is a tooltip to display
1679 if( tooltip_text && *tooltip_text ) {
1680 // Knightly : display tooltip when current owner is invalid or when it is within visible duration
1681 uint32 elapsed_time;
1682 if( !tooltip_owner || ((elapsed_time=dr_time()-tooltip_register_time)>env_t::tooltip_delay && elapsed_time<=env_t::tooltip_delay+env_t::tooltip_duration) ) {
1683 const sint16 width = proportional_string_width(tooltip_text)+7;
1684 display_ddd_proportional_clip(min(tooltip_xpos,disp_width-width), max(menu_height+7,tooltip_ypos), width, 0, env_t::tooltip_color, env_t::tooltip_textcolor, tooltip_text, true);
1685 if(wl) {
1686 wl->set_background_dirty();
1687 }
1688 }
1689 }
1690 else if(static_tooltip_text!=NULL && *static_tooltip_text) {
1691 const sint16 width = proportional_string_width(static_tooltip_text)+7;
1692 display_ddd_proportional_clip(min(get_mouse_x()+16,disp_width-width), max(menu_height+7,get_mouse_y()-16), width, 0, env_t::tooltip_color, env_t::tooltip_textcolor, static_tooltip_text, true);
1693 if(wl) {
1694 wl->set_background_dirty();
1695 }
1696 }
1697 // Knightly : reset owner and group if no tooltip has been registered
1698 if( !tooltip_text ) {
1699 tooltip_owner = 0;
1700 tooltip_group = 0;
1701 }
1702 // Hajo : clear tooltip text to avoid sticky tooltips
1703 tooltip_text = 0;
1704 }
1705
1706 POP_CLIP();
1707
1708 if(!wl) {
1709 // no infos during loading etc
1710 return;
1711 }
1712 }
1713
1714 char const *time = tick_to_string( wl->get_ticks(), true );
1715
1716 // statusbar background
1717 KOORD_VAL const status_bar_height = win_get_statusbar_height();
1718 KOORD_VAL const status_bar_y = disp_height - status_bar_height;
1719 KOORD_VAL const status_bar_text_y = status_bar_y + (status_bar_height - LINESPACE) / 2;
1720 KOORD_VAL const status_bar_icon_y = status_bar_y + (status_bar_height - 15) / 2;
1721 display_set_clip_wh( 0, 0, disp_width, disp_height );
1722 display_fillbox_wh_rgb(0, status_bar_y - 1, disp_width, 1, SYSCOL_STATUSBAR_DIVIDER, false);
1723 display_fillbox_wh_rgb(0, status_bar_y, disp_width, status_bar_height, SYSCOL_STATUSBAR_BACKGROUND, false);
1724
1725 bool tooltip_check = get_mouse_y() > status_bar_y;
1726 if( tooltip_check ) {
1727 tooltip_xpos = get_mouse_x();
1728 tooltip_ypos = status_bar_y-10-TICKER_HEIGHT*show_ticker;
1729 }
1730
1731 // season color
1732 display_color_img( skinverwaltung_t::seasons_icons->get_image_id(wl->get_season()), 2, status_bar_icon_y, 0, false, true );
1733 if( tooltip_check && tooltip_xpos<14 ) {
1734 static char const* const seasons[] = { "q2", "q3", "q4", "q1" };
1735 tooltip_text = translator::translate(seasons[wl->get_season()]);
1736 tooltip_check = false;
1737 }
1738
1739 scr_coord_val right_border = disp_width-4;
1740
1741 // shown if timeline game
1742 if( wl->use_timeline() && skinverwaltung_t::timelinesymbol ) {
1743 right_border -= 14;
1744 display_color_img( skinverwaltung_t::timelinesymbol->get_image_id(0), right_border, status_bar_icon_y, 0, false, true );
1745 if( tooltip_check && tooltip_xpos>=right_border ) {
1746 tooltip_text = translator::translate("timeline");
1747 tooltip_check = false;
1748 }
1749 }
1750
1751 // shown if connected
1752 if( env_t::networkmode && skinverwaltung_t::networksymbol ) {
1753 right_border -= 14;
1754 display_color_img( skinverwaltung_t::networksymbol->get_image_id(0), right_border, status_bar_icon_y, 0, false, true );
1755 if( tooltip_check && tooltip_xpos>=right_border ) {
1756 tooltip_text = translator::translate("Connected with server");
1757 tooltip_check = false;
1758 }
1759 }
1760
1761 // put pause icon
1762 if( wl->is_paused() && skinverwaltung_t::pausesymbol ) {
1763 right_border -= 14;
1764 display_color_img( skinverwaltung_t::pausesymbol->get_image_id(0), right_border, status_bar_icon_y, 0, false, true );
1765 if( tooltip_check && tooltip_xpos>=right_border ) {
1766 tooltip_text = translator::translate("GAME PAUSED");
1767 tooltip_check = false;
1768 }
1769 }
1770
1771 // put fast forward icon
1772 if( wl->is_fast_forward() && skinverwaltung_t::fastforwardsymbol ) {
1773 right_border -= 14;
1774 display_color_img( skinverwaltung_t::fastforwardsymbol->get_image_id(0), right_border, status_bar_icon_y, 0, false, true );
1775 if( tooltip_check && tooltip_xpos>=right_border ) {
1776 tooltip_text = translator::translate("Fast forward");
1777 tooltip_check = false;
1778 }
1779 }
1780
1781 koord3d pos = wl->get_zeiger()->get_pos();
1782
1783 static cbuffer_t info;
1784 info.clear();
1785 if( pos!=koord3d::invalid ) {
1786 info.printf( "(%s)", pos.get_str() );
1787 }
1788 if( skinverwaltung_t::timelinesymbol==NULL ) {
1789 info.printf( " %s", translator::translate(wl->use_timeline()?"timeline":"no timeline") );
1790 }
1791 if(wl->show_distance!=koord3d::invalid && wl->show_distance!=pos) {
1792 info.printf("-(%d,%d)", wl->show_distance.x-pos.x, wl->show_distance.y-pos.y );
1793 }
1794 if( !env_t::networkmode ) {
1795 // time multiplier text
1796 if(wl->is_fast_forward()) {
1797 info.printf(" %s(T~%1.2f)", skinverwaltung_t::fastforwardsymbol?"":">> ", wl->get_simloops()/50.0 );
1798 }
1799 else if(!wl->is_paused()) {
1800 info.printf(" (T=%1.2f)", wl->get_time_multiplier()/16.0 );
1801 }
1802 else if( skinverwaltung_t::pausesymbol==NULL ) {
1803 info.printf( " %s", translator::translate("GAME PAUSED") );
1804 }
1805 }
1806 #ifdef DEBUG
1807 if( env_t::verbose_debug>3 ) {
1808 if( haltestelle_t::get_rerouting_status()==RECONNECTING ) {
1809 info.append( " +" );
1810 }
1811 else if( haltestelle_t::get_rerouting_status()==REROUTING ) {
1812 info.append( " *" );
1813 }
1814 if( skinverwaltung_t::compass_iso == NULL && wl->get_settings().get_rotation() ) {
1815 static const char *compass_dir[4] = { "North", "East", "South", "West" };
1816 info.append( " " );
1817 info.append( translator::translate( compass_dir[ 4-wl->get_settings().get_rotation() ] ) );
1818 }
1819 }
1820 #endif
1821 scr_coord_val w_left = 20+display_proportional_rgb(20, status_bar_text_y, time, ALIGN_LEFT, SYSCOL_STATUSBAR_TEXT, true);
1822 scr_coord_val w_right = display_proportional_rgb(right_border-4, status_bar_text_y, info, ALIGN_RIGHT, SYSCOL_STATUSBAR_TEXT, true);
1823 scr_coord_val middle = (disp_width+((w_left+8)&0xFFF0)-((w_right+8)&0xFFF0))/2;
1824
1825 if(wl->get_active_player()) {
1826 char buffer[256];
1827 display_proportional_rgb( middle-5, status_bar_text_y, wl->get_active_player()->get_name(), ALIGN_RIGHT, PLAYER_FLAG|color_idx_to_rgb(wl->get_active_player()->get_player_color1()+0), true);
1828 money_to_string(buffer, konto );
1829 display_proportional_rgb( middle+5, status_bar_text_y, buffer, ALIGN_LEFT, konto >= 0.0?MONEY_PLUS:MONEY_MINUS, true);
1830 }
1831 }
1832
1833
win_set_world(karte_t * world)1834 void win_set_world(karte_t *world)
1835 {
1836 wl = world;
1837 // remove all save window positions
1838 old_win_pos.clear();
1839 }
1840
1841
win_redraw_world()1842 void win_redraw_world()
1843 {
1844 if(wl) {
1845 wl->set_dirty();
1846 }
1847 }
1848
1849
win_change_zoom_factor(bool magnify)1850 bool win_change_zoom_factor(bool magnify)
1851 {
1852 const bool result = magnify ? zoom_factor_up() : zoom_factor_down();
1853
1854 wl->get_viewport()->metrics_updated();
1855
1856 return result;
1857 }
1858
1859
win_load_font(const char * fname,uint16 fontsize)1860 void win_load_font(const char *fname, uint16 fontsize)
1861 {
1862 bool force_reload = fontsize != env_t::fontsize;
1863 env_t::fontsize = fontsize;
1864
1865 if (display_load_font(fname, force_reload) ) {
1866 // successfull
1867 gui_theme_t::themes_init( env_t::default_theme, false );
1868
1869 event_t *ev = new event_t();
1870 ev->ev_class = EVENT_SYSTEM;
1871 ev->ev_code = SYSTEM_THEME_CHANGED;
1872 queue_event( ev );
1873 }
1874 else {
1875 // restore old font
1876 display_load_font(env_t::fontname.c_str(), true);
1877 }
1878 win_redraw_world();
1879 }
1880
1881
1882 /**
1883 * Sets the tooltip to display.
1884 * Has to be called from within gui_frame_t::draw
1885 * @param owner : owner==NULL disables timing (initial delay and visible duration)
1886 * @author Hj. Malthaner, Knightly
1887 */
win_set_tooltip(int xpos,int ypos,const char * text,const void * const owner,const void * const group)1888 void win_set_tooltip(int xpos, int ypos, const char *text, const void *const owner, const void *const group)
1889 {
1890 // check whether the right window will set the tooltip
1891 if (inside_event_handling != tooltip_element) {
1892 return;
1893 }
1894 // must be set every time as win_display_flush() will reset them
1895 tooltip_text = text;
1896
1897 // update ownership if changed
1898 if( owner!=tooltip_owner ) {
1899 tooltip_owner = owner;
1900 // update register time only if owner is valid
1901 if( owner ) {
1902 const uint32 current_time = dr_time();
1903 if( group && group==tooltip_group ) {
1904 // case : same group
1905 const uint32 elapsed_time = current_time - tooltip_register_time;
1906 const uint32 threshold = env_t::tooltip_delay - (env_t::tooltip_delay>>2); // 3/4 of delay
1907 if( elapsed_time>threshold && elapsed_time<=env_t::tooltip_delay+env_t::tooltip_duration ) {
1908 // case : threshold was reached and duration not expired -> delay time is reduced to 1/4
1909 tooltip_register_time = current_time - threshold;
1910 }
1911 else {
1912 // case : either before threshold or duration expired
1913 tooltip_register_time = current_time;
1914 }
1915 }
1916 else {
1917 // case : owner has no associated group or group is different -> simply reset to current time
1918 tooltip_group = group;
1919 tooltip_register_time = current_time;
1920 }
1921 }
1922 else {
1923 // no owner to associate with a group even if the group is valid
1924 tooltip_group = 0;
1925 }
1926 }
1927
1928 tooltip_xpos = xpos;
1929 tooltip_ypos = ypos;
1930 }
1931
1932
1933 /**
1934 * Sets the tooltip to display.
1935 * @author Hj. Malthaner
1936 */
win_set_static_tooltip(const char * text)1937 void win_set_static_tooltip(const char *text)
1938 {
1939 static_tooltip_text = text;
1940 }
1941