1 // Copyright (c) Warwick Allison, 1999.
2 // Qt4 conversion copyright (c) Ray Chason, 2012-2014.
3 // NetHack may be freely redistributed.  See license for details.
4 
5 // qt4map.cpp -- the map window
6 
7 extern "C" {
8 #include "hack.h"
9 }
10 #undef Invisible
11 #undef Warning
12 #undef index
13 #undef msleep
14 #undef rindex
15 #undef wizard
16 #undef yn
17 #undef min
18 #undef max
19 
20 #include <QtGui/QtGui>
21 #if QT_VERSION >= 0x050000
22 #include <QtWidgets/QtWidgets>
23 #endif
24 #include "qt4map.h"
25 #include "qt4map.moc"
26 #include "qt4click.h"
27 #include "qt4glyph.h"
28 #include "qt_xpms.h"
29 #include "qt4set.h"
30 #include "qt4str.h"
31 
32 // temporary
33 extern int qt_compact_mode;
34 // end temporary
35 
36 namespace nethack_qt4 {
37 
38 #ifdef TEXTCOLOR
nhcolor_to_pen(int c)39 static const QPen& nhcolor_to_pen(int c)
40 {
41     static QPen* pen=0;
42     if ( !pen ) {
43 	pen = new QPen[17];
44 	pen[0] = QColor(64,64,64);
45 	pen[1] = QColor(Qt::red);
46 	pen[2] = QColor(0,191,0);
47 	pen[3] = QColor(127,127,0);
48 	pen[4] = QColor(Qt::blue);
49 	pen[5] = QColor(Qt::magenta);
50 	pen[6] = QColor(Qt::cyan);
51 	pen[7] = QColor(Qt::gray);
52 	pen[8] = QColor(Qt::white); // no color
53 	pen[9] = QColor(255,127,0);
54 	pen[10] = QColor(127,255,127);
55 	pen[11] = QColor(Qt::yellow);
56 	pen[12] = QColor(127,127,255);
57 	pen[13] = QColor(255,127,255);
58 	pen[14] = QColor(127,255,255);
59 	pen[15] = QColor(Qt::white);
60 	pen[16] = QColor(Qt::black);
61     }
62 
63     return pen[c];
64 }
65 #endif
66 
NetHackQtMapViewport(NetHackQtClickBuffer & click_sink)67 NetHackQtMapViewport::NetHackQtMapViewport(NetHackQtClickBuffer& click_sink) :
68 	QWidget(NULL),
69 	rogue_font(NULL),
70 	clicksink(click_sink),
71 	change(10)
72 {
73     pet_annotation = QPixmap(qt_compact_mode ? pet_mark_small_xpm : pet_mark_xpm);
74     pile_annotation = QPixmap(pile_mark_xpm);
75 
76     Clear();
77     cursor.setX(0);
78     cursor.setY(0);
79 }
80 
~NetHackQtMapViewport(void)81 NetHackQtMapViewport::~NetHackQtMapViewport(void)
82 {
83     delete rogue_font;
84 }
85 
paintEvent(QPaintEvent * event)86 void NetHackQtMapViewport::paintEvent(QPaintEvent* event)
87 {
88     QRect area=event->rect();
89     QRect garea;
90     garea.setCoords(
91 	std::max(0,area.left()/qt_settings->glyphs().width()),
92 	std::max(0,area.top()/qt_settings->glyphs().height()),
93 	std::min(COLNO-1,area.right()/qt_settings->glyphs().width()),
94 	std::min(ROWNO-1,area.bottom()/qt_settings->glyphs().height())
95     );
96 
97     QPainter painter;
98 
99     painter.begin(this);
100 
101     if (Is_rogue_level(&u.uz) || iflags.wc_ascii_map) {
102 	// You enter a VERY primitive world!
103 
104 	painter.setClipRect( event->rect() ); // (normally we don't clip)
105 	painter.fillRect( event->rect(), Qt::black );
106 
107 	if ( !rogue_font ) {
108 	    // Find font...
109 	    int pts = 5;
110 	    QString fontfamily = iflags.wc_font_map
111 		? iflags.wc_font_map : "Monospace";
112 	    bool bold = false;
113 	    if ( fontfamily.right(5).toLower() == "-bold" ) {
114 		fontfamily.truncate(fontfamily.length()-5);
115 		bold = true;
116 	    }
117 	    while ( pts < 32 ) {
118 		QFont f(fontfamily, pts, bold ? QFont::Bold : QFont::Normal);
119 		painter.setFont(QFont(fontfamily, pts));
120 		QFontMetrics fm = painter.fontMetrics();
121 		if ( fm.width("M") > qt_settings->glyphs().width() )
122 		    break;
123 		if ( fm.height() > qt_settings->glyphs().height() )
124 		    break;
125 		pts++;
126 	    }
127 	    rogue_font = new QFont(fontfamily,pts-1);
128 	}
129 	painter.setFont(*rogue_font);
130 
131 	for (int j=garea.top(); j<=garea.bottom(); j++) {
132 	    for (int i=garea.left(); i<=garea.right(); i++) {
133 		unsigned short g=Glyph(i,j);
134 		int color;
135 		int ch;
136 		unsigned special;
137 
138 		painter.setPen( Qt::green );
139 		/* map glyph to character and color */
140 		mapglyph(g, &ch, &color, &special, i, j, 0);
141 		ch = cp437(ch);
142 #ifdef TEXTCOLOR
143 		painter.setPen( nhcolor_to_pen(color) );
144 #endif
145 		if (!DrawWalls(
146 			painter,
147 			i*qt_settings->glyphs().width(),
148 			j*qt_settings->glyphs().height(),
149 			qt_settings->glyphs().width(),
150 			qt_settings->glyphs().height(),
151 			ch)) {
152 		    painter.drawText(
153 			i*qt_settings->glyphs().width(),
154 			j*qt_settings->glyphs().height(),
155 			qt_settings->glyphs().width(),
156 			qt_settings->glyphs().height(),
157 			Qt::AlignCenter,
158 			QString(QChar(ch)).left(1)
159 		    );
160 		}
161 #ifdef TEXTCOLOR
162 		if (((special & MG_PET) != 0) && ::iflags.hilite_pet) {
163                     painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation);
164                 } else if (((special & MG_OBJPILE) != 0) && ::iflags.hilite_pile) {
165                     painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pile_annotation);
166                 }
167 #endif
168             }
169         }
170 
171 	painter.setFont(font());
172     } else {
173 	for (int j=garea.top(); j<=garea.bottom(); j++) {
174 	    for (int i=garea.left(); i<=garea.right(); i++) {
175 		unsigned short g=Glyph(i,j);
176 		int color;
177 		int ch;
178 		unsigned special;
179 		mapglyph(g, &ch, &color, &special, i, j, 0);
180 		qt_settings->glyphs().drawCell(painter, g, i, j);
181 #ifdef TEXTCOLOR
182 		if (((special & MG_PET) != 0) && ::iflags.hilite_pet) {
183                     painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation);
184                 } else if (((special & MG_OBJPILE) != 0) && ::iflags.hilite_pile) {
185                     painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pile_annotation);
186                 }
187 #endif
188 	    }
189 	}
190     }
191 
192     if (garea.contains(cursor)) {
193 	if (Is_rogue_level(&u.uz)) {
194 #ifdef TEXTCOLOR
195 	    painter.setPen( Qt::white );
196 #else
197 	    painter.setPen( Qt::green ); // REALLY primitive
198 #endif
199 	} else
200 	{
201 	    int hp100;
202 	    if (u.mtimedone) {
203 		hp100=u.mhmax ? u.mh*100/u.mhmax : 100;
204 	    } else {
205 		hp100=u.uhpmax ? u.uhp*100/u.uhpmax : 100;
206 	    }
207 
208 	    if (hp100 > 75) painter.setPen(Qt::white);
209 	    else if (hp100 > 50) painter.setPen(Qt::yellow);
210 	    else if (hp100 > 25) painter.setPen(QColor(0xff,0xbf,0x00)); // orange
211 	    else if (hp100 > 10) painter.setPen(Qt::red);
212 	    else painter.setPen(Qt::magenta);
213 	}
214 
215 	painter.drawRect(
216 	    cursor.x()*qt_settings->glyphs().width(),cursor.y()*qt_settings->glyphs().height(),
217 	    qt_settings->glyphs().width()-1,qt_settings->glyphs().height()-1);
218     }
219 
220 #if 0
221     if (area.intersects(messages_rect)) {
222 	painter.setPen(Qt::black);
223 	painter.drawText(viewport.contentsX()+1,viewport.contentsY()+1,
224 	    viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages);
225 	painter.setPen(Qt::white);
226 	painter.drawText(viewport.contentsX(),viewport.contentsY(),
227 	    viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages);
228     }
229 #endif
230 
231     painter.end();
232 }
233 
DrawWalls(QPainter & painter,int x,int y,int w,int h,unsigned ch)234 bool NetHackQtMapViewport::DrawWalls(
235 	QPainter& painter,
236 	int x, int y, int w, int h,
237 	unsigned ch)
238 {
239     enum
240     {
241 	w_left      = 0x01,
242 	w_right     = 0x02,
243 	w_up        = 0x04,
244 	w_down      = 0x08,
245 	w_sq_top    = 0x10,
246 	w_sq_bottom = 0x20,
247 	w_sq_left   = 0x40,
248 	w_sq_right  = 0x80
249     };
250     unsigned linewidth;
251     unsigned walls;
252     int x1, y1, x2, y2, x3, y3;
253 
254     linewidth = ((w < h) ? w : h)/8;
255     if (linewidth == 0) linewidth = 1;
256 
257     // Single walls
258     walls = 0;
259     switch (ch)
260     {
261     case 0x2500: // BOX DRAWINGS LIGHT HORIZONTAL
262 	walls = w_left | w_right;
263 	break;
264 
265     case 0x2502: // BOX DRAWINGS LIGHT VERTICAL
266 	walls = w_up | w_down;
267 	break;
268 
269     case 0x250C: // BOX DRAWINGS LIGHT DOWN AND RIGHT
270 	walls = w_down | w_right;
271 	break;
272 
273     case 0x2510: // BOX DRAWINGS LIGHT DOWN AND LEFT
274 	walls = w_down | w_left;
275 	break;
276 
277     case 0x2514: // BOX DRAWINGS LIGHT UP AND RIGHT
278 	walls = w_up | w_right;
279 	break;
280 
281     case 0x2518: // BOX DRAWINGS LIGHT UP AND LEFT
282 	walls = w_up | w_left;
283 	break;
284 
285     case 0x251C: // BOX DRAWINGS LIGHT VERTICAL AND RIGHT
286 	walls = w_up | w_down | w_right;
287 	break;
288 
289     case 0x2524: // BOX DRAWINGS LIGHT VERTICAL AND LEFT
290 	walls = w_up | w_down | w_left;
291 	break;
292 
293     case 0x252C: // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
294 	walls = w_down | w_left | w_right;
295 	break;
296 
297     case 0x2534: // BOX DRAWINGS LIGHT UP AND HORIZONTAL
298 	walls = w_up | w_left | w_right;
299 	break;
300 
301     case 0x253C: // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
302 	walls = w_up | w_down | w_left | w_right;
303 	break;
304     }
305 
306     if (walls != 0)
307     {
308 	x1 = x + w/2;
309 	switch (walls & (w_up | w_down))
310 	{
311 	case w_up:
312 	    painter.drawLine(x1, y, x1, y+h/2);
313 	    break;
314 
315 	case w_down:
316 	    painter.drawLine(x1, y+h/2, x1, y+h-1);
317 	    break;
318 
319 	case w_up | w_down:
320 	    painter.drawLine(x1, y, x1, y+h-1);
321 	    break;
322 	}
323 
324 	y1 = y + h/2;
325 	switch (walls & (w_left | w_right))
326 	{
327 	case w_left:
328 	    painter.drawLine(x, y1, x+w/2, y1);
329 	    break;
330 
331 	case w_right:
332 	    painter.drawLine(x+w/2, y1, x+w-1, y1);
333 	    break;
334 
335 	case w_left | w_right:
336 	    painter.drawLine(x, y1, x+w-1, y1);
337 	    break;
338 	}
339 
340 	return true;
341     }
342 
343     // Double walls
344     walls = 0;
345     switch (ch)
346     {
347     case 0x2550: // BOX DRAWINGS DOUBLE HORIZONTAL
348 	walls = w_left | w_right | w_sq_top | w_sq_bottom;
349 	break;
350 
351     case 0x2551: // BOX DRAWINGS DOUBLE VERTICAL
352 	walls = w_up | w_down | w_sq_left | w_sq_right;
353 	break;
354 
355     case 0x2554: // BOX DRAWINGS DOUBLE DOWN AND RIGHT
356 	walls = w_down | w_right | w_sq_top | w_sq_left;
357 	break;
358 
359     case 0x2557: // BOX DRAWINGS DOUBLE DOWN AND LEFT
360 	walls = w_down | w_left | w_sq_top | w_sq_right;
361 	break;
362 
363     case 0x255A: // BOX DRAWINGS DOUBLE UP AND RIGHT
364 	walls = w_up | w_right | w_sq_bottom | w_sq_left;
365 	break;
366 
367     case 0x255D: // BOX DRAWINGS DOUBLE UP AND LEFT
368 	walls = w_up | w_left | w_sq_bottom | w_sq_right;
369 	break;
370 
371     case 0x2560: // BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
372 	walls = w_up | w_down | w_right | w_sq_left;
373 	break;
374 
375     case 0x2563: // BOX DRAWINGS DOUBLE VERTICAL AND LEFT
376 	walls = w_up | w_down | w_left | w_sq_right;
377 	break;
378 
379     case 0x2566: // BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
380 	walls = w_down | w_left | w_right | w_sq_top;
381 	break;
382 
383     case 0x2569: // BOX DRAWINGS DOUBLE UP AND HORIZONTAL
384 	walls = w_up | w_left | w_right | w_sq_bottom;
385 	break;
386 
387     case 0x256C: // BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
388 	walls = w_up | w_down | w_left | w_right;
389 	break;
390     }
391     if (walls != 0)
392     {
393 	x1 = x + w/2 - linewidth;
394 	x2 = x + w/2 + linewidth;
395 	x3 = x + w - 1;
396 	y1 = y + h/2 - linewidth;
397 	y2 = y + h/2 + linewidth;
398 	y3 = y + h - 1;
399 	if (walls & w_up)
400 	{
401 	    painter.drawLine(x1, y, x1, y1);
402 	    painter.drawLine(x2, y, x2, y1);
403 	}
404 	if (walls & w_down)
405 	{
406 	    painter.drawLine(x1, y2, x1, y3);
407 	    painter.drawLine(x2, y2, x2, y3);
408 	}
409 	if (walls & w_left)
410 	{
411 	    painter.drawLine(x, y1, x1, y1);
412 	    painter.drawLine(x, y2, x1, y2);
413 	}
414 	if (walls & w_right)
415 	{
416 	    painter.drawLine(x2, y1, x3, y1);
417 	    painter.drawLine(x2, y2, x3, y2);
418 	}
419 	if (walls & w_sq_top)
420 	{
421 	    painter.drawLine(x1, y1, x2, y1);
422 	}
423 	if (walls & w_sq_bottom)
424 	{
425 	    painter.drawLine(x1, y2, x2, y2);
426 	}
427 	if (walls & w_sq_left)
428 	{
429 	    painter.drawLine(x1, y1, x1, y2);
430 	}
431 	if (walls & w_sq_right)
432 	{
433 	    painter.drawLine(x2, y1, x2, y2);
434 	}
435 	return true;
436     }
437 
438     // Solid blocks
439     if (0x2591 <= ch && ch <= 0x2593)
440     {
441 	unsigned shade = ch - 0x2590;
442 	QColor rgb(painter.pen().color());
443 	QColor rgb2(
444 		rgb.red()*shade/4,
445 		rgb.green()*shade/4,
446 		rgb.blue()*shade/4);
447 	painter.fillRect(x, y, w, h, rgb2);
448 	return true;
449     }
450 
451     return false;
452 }
453 
mousePressEvent(QMouseEvent * event)454 void NetHackQtMapViewport::mousePressEvent(QMouseEvent* event)
455 {
456     clicksink.Put(
457 	event->pos().x()/qt_settings->glyphs().width(),
458 	event->pos().y()/qt_settings->glyphs().height(),
459 	event->button()==Qt::LeftButton ? CLICK_1 : CLICK_2
460     );
461     qApp->exit();
462 }
463 
updateTiles()464 void NetHackQtMapViewport::updateTiles()
465 {
466     change.clear();
467     change.add(0,0,COLNO,ROWNO);
468     delete rogue_font; rogue_font = NULL;
469 }
470 
sizeHint() const471 QSize NetHackQtMapViewport::sizeHint() const
472 {
473     return QSize(
474 	    qt_settings->glyphs().width() * COLNO,
475 	    qt_settings->glyphs().height() * ROWNO);
476 }
477 
minimumSizeHint() const478 QSize NetHackQtMapViewport::minimumSizeHint() const
479 {
480     return sizeHint();
481 }
482 
clickCursor()483 void NetHackQtMapViewport::clickCursor()
484 {
485     clicksink.Put(cursor.x(),cursor.y(),CLICK_1);
486     qApp->exit();
487 }
488 
Clear()489 void NetHackQtMapViewport::Clear()
490 {
491     unsigned short stone=cmap_to_glyph(S_stone);
492 
493     for (int j=0; j<ROWNO; j++) {
494 	for (int i=0; i<COLNO; i++) {
495 	    Glyph(i,j)=stone;
496 	}
497     }
498 
499     change.clear();
500     change.add(0,0,COLNO,ROWNO);
501 }
502 
Display(bool block)503 void NetHackQtMapViewport::Display(bool block)
504 {
505     for (int i=0; i<change.clusters(); i++) {
506 	const QRect& ch=change[i];
507 	repaint(
508 	    ch.x()*qt_settings->glyphs().width(),
509 	    ch.y()*qt_settings->glyphs().height(),
510 	    ch.width()*qt_settings->glyphs().width(),
511 	    ch.height()*qt_settings->glyphs().height()
512 	);
513     }
514 
515     change.clear();
516 
517     if (block) {
518 	yn_function("Press a key when done viewing",0,'\0');
519     }
520 }
521 
CursorTo(int x,int y)522 void NetHackQtMapViewport::CursorTo(int x,int y)
523 {
524     Changed(cursor.x(),cursor.y());
525     cursor.setX(x);
526     cursor.setY(y);
527     Changed(cursor.x(),cursor.y());
528 }
529 
PrintGlyph(int x,int y,int glyph)530 void NetHackQtMapViewport::PrintGlyph(int x,int y,int glyph)
531 {
532     Glyph(x,y)=glyph;
533     Changed(x,y);
534 }
535 
Changed(int x,int y)536 void NetHackQtMapViewport::Changed(int x, int y)
537 {
538     change.add(x,y);
539 }
540 
NetHackQtMapWindow2(NetHackQtClickBuffer & click_sink)541 NetHackQtMapWindow2::NetHackQtMapWindow2(NetHackQtClickBuffer& click_sink) :
542 	QScrollArea(NULL),
543 	m_viewport(new NetHackQtMapViewport(click_sink))
544 {
545     QPalette palette;
546     palette.setColor(backgroundRole(), Qt::black);
547     setPalette(palette);
548 
549     setWidget(m_viewport);
550 
551     connect(qt_settings,SIGNAL(tilesChanged()),this,SLOT(updateTiles()));
552     updateTiles();
553 }
554 
updateTiles()555 void NetHackQtMapWindow2::updateTiles()
556 {
557     NetHackQtGlyphs& glyphs = qt_settings->glyphs();
558     int gw = glyphs.width();
559     int gh = glyphs.height();
560     // Be exactly the size we want to be - full map...
561     m_viewport->resize(COLNO*gw,ROWNO*gh);
562 
563     verticalScrollBar()->setSingleStep(gh);
564     verticalScrollBar()->setPageStep(gh);
565     horizontalScrollBar()->setSingleStep(gw);
566     horizontalScrollBar()->setPageStep(gw);
567 
568     m_viewport->updateTiles();
569     Display(false);
570 
571     emit resized();
572 }
573 
clearMessages()574 void NetHackQtMapWindow2::clearMessages()
575 {
576     messages = "";
577     update(messages_rect);
578     messages_rect = QRect();
579 }
580 
putMessage(int attr,const QString & text)581 void NetHackQtMapWindow2::putMessage(int attr, const QString& text)
582 {
583     if ( !messages.isEmpty() )
584 	messages += "\n";
585     messages += QString(text).replace(QChar(0x200B), "");
586     QFontMetrics fm = fontMetrics();
587 #if 0
588     messages_rect = fm.boundingRect(viewport.contentsX(),viewport.contentsY(),viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages);
589     update(messages_rect);
590 #endif
591 }
592 
clickCursor()593 void NetHackQtMapWindow2::clickCursor()
594 {
595     m_viewport->clickCursor();
596 }
597 
Widget()598 QWidget *NetHackQtMapWindow2::Widget()
599 {
600     return this;
601 }
602 
Clear()603 void NetHackQtMapWindow2::Clear()
604 {
605     m_viewport->Clear();
606 }
607 
Display(bool block)608 void NetHackQtMapWindow2::Display(bool block)
609 {
610     m_viewport->Display(block);
611 }
612 
CursorTo(int x,int y)613 void NetHackQtMapWindow2::CursorTo(int x,int y)
614 {
615     m_viewport->CursorTo(x, y);
616 }
617 
PutStr(int attr,const QString & text)618 void NetHackQtMapWindow2::PutStr(int attr, const QString& text)
619 {
620     puts("unexpected PutStr in MapWindow");
621 }
622 
ClipAround(int x,int y)623 void NetHackQtMapWindow2::ClipAround(int x,int y)
624 {
625     // Convert to pixel of center of tile
626     x=x*qt_settings->glyphs().width()+qt_settings->glyphs().width()/2;
627     y=y*qt_settings->glyphs().height()+qt_settings->glyphs().height()/2;
628 
629     // Then ensure that pixel is visible
630     ensureVisible(x,y,width()*0.45,height()*0.45);
631 }
632 
PrintGlyph(int x,int y,int glyph)633 void NetHackQtMapWindow2::PrintGlyph(int x,int y,int glyph)
634 {
635     m_viewport->PrintGlyph(x, y, glyph);
636 }
637 
638 #if 0 //RLC
639 // XXX Hmmm... crash after saving bones file if Map window is
640 // XXX deleted.  Strange bug somewhere.
641 bool NetHackQtMapWindow::Destroy() { return false; }
642 
643 NetHackQtMapWindow::NetHackQtMapWindow(NetHackQtClickBuffer& click_sink) :
644     clicksink(click_sink),
645     change(10),
646     rogue_font(0)
647 {
648     viewport.addChild(this);
649 
650     QPalette palette;
651     palette.setColor(backgroundRole(), Qt::black);
652     setPalette(palette);
653     palette.setColor(viewport.backgroundRole(), Qt::black);
654     viewport.setPalette(palette);
655 
656     pet_annotation = QPixmap(qt_compact_mode ? pet_mark_small_xpm : pet_mark_xpm);
657     pile_annotation = QPixmap(pile_mark_xpm);
658 
659     cursor.setX(0);
660     cursor.setY(0);
661     Clear();
662 
663     connect(qt_settings,SIGNAL(tilesChanged()),this,SLOT(updateTiles()));
664     connect(&viewport, SIGNAL(contentsMoving(int,int)), this,
665 		SLOT(moveMessages(int,int)));
666 
667     updateTiles();
668     //setFocusPolicy(Qt::StrongFocus);
669 }
670 
671 void NetHackQtMapWindow::moveMessages(int x, int y)
672 {
673     QRect u = messages_rect;
674     messages_rect.moveTopLeft(QPoint(x,y));
675     u |= messages_rect;
676     update(u);
677 }
678 
679 void NetHackQtMapWindow::clearMessages()
680 {
681     messages = "";
682     update(messages_rect);
683     messages_rect = QRect();
684 }
685 
686 void NetHackQtMapWindow::putMessage(int attr, const QString& text)
687 {
688     if ( !messages.isEmpty() )
689 	messages += "\n";
690     messages += QString(text).replace(QChar(0x200B), "");
691     QFontMetrics fm = fontMetrics();
692     messages_rect = fm.boundingRect(viewport.contentsX(),viewport.contentsY(),viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages);
693     update(messages_rect);
694 }
695 
696 void NetHackQtMapWindow::updateTiles()
697 {
698     NetHackQtGlyphs& glyphs = qt_settings->glyphs();
699     int gw = glyphs.width();
700     int gh = glyphs.height();
701     // Be exactly the size we want to be - full map...
702     resize(COLNO*gw,ROWNO*gh);
703 
704     viewport.verticalScrollBar()->setSingleStep(gh);
705     viewport.verticalScrollBar()->setPageStep(gh);
706     viewport.horizontalScrollBar()->setSingleStep(gw);
707     viewport.horizontalScrollBar()->setPageStep(gw);
708     /*
709     viewport.setMaximumSize(
710 	gw*COLNO + viewport.verticalScrollBar()->width(),
711 	gh*ROWNO + viewport.horizontalScrollBar()->height()
712     );
713     */
714     viewport.updateScrollBars();
715 
716     change.clear();
717     change.add(0,0,COLNO,ROWNO);
718     delete rogue_font; rogue_font = 0;
719     Display(false);
720 
721     emit resized();
722 }
723 
724 NetHackQtMapWindow::~NetHackQtMapWindow()
725 {
726     // Remove from viewport porthole, since that is a destructible member.
727     viewport.removeChild(this);
728     setParent(0,0);
729 }
730 
731 QWidget* NetHackQtMapWindow::Widget()
732 {
733     return &viewport;
734 }
735 
736 void NetHackQtMapWindow::Scroll(int dx, int dy)
737 {
738     if (viewport.horizontalScrollBar()->isVisible()) {
739 	while (dx<0) { viewport.horizontalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub); dx++; }
740 	while (dx>0) { viewport.horizontalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd); dx--; }
741     }
742     if (viewport.verticalScrollBar()->isVisible()) {
743 	while (dy<0) { viewport.verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub); dy++; }
744 	while (dy>0) { viewport.verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd); dy--; }
745     }
746 }
747 
748 void NetHackQtMapWindow::Clear()
749 {
750     unsigned short stone=cmap_to_glyph(S_stone);
751 
752     for (int j=0; j<ROWNO; j++) {
753 	for (int i=0; i<COLNO; i++) {
754 	    Glyph(i,j)=stone;
755 	}
756     }
757 
758     change.clear();
759     change.add(0,0,COLNO,ROWNO);
760 }
761 
762 void NetHackQtMapWindow::clickCursor()
763 {
764     clicksink.Put(cursor.x(),cursor.y(),CLICK_1);
765     qApp->exit();
766 }
767 
768 void NetHackQtMapWindow::mousePressEvent(QMouseEvent* event)
769 {
770     clicksink.Put(
771 	event->pos().x()/qt_settings->glyphs().width(),
772 	event->pos().y()/qt_settings->glyphs().height(),
773 	event->button()==Qt::LeftButton ? CLICK_1 : CLICK_2
774     );
775     qApp->exit();
776 }
777 
778 void NetHackQtMapWindow::paintEvent(QPaintEvent* event)
779 {
780     QRect area=event->rect();
781     QRect garea;
782     garea.setCoords(
783 	std::max(0,area.left()/qt_settings->glyphs().width()),
784 	std::max(0,area.top()/qt_settings->glyphs().height()),
785 	std::min(COLNO-1,area.right()/qt_settings->glyphs().width()),
786 	std::min(ROWNO-1,area.bottom()/qt_settings->glyphs().height())
787     );
788 
789     QPainter painter;
790 
791     painter.begin(this);
792 
793     if (is_rogue_level(&u.uz) || iflags.wc_ascii_map) {
794 	// You enter a VERY primitive world!
795 
796 	painter.setClipRect( event->rect() ); // (normally we don't clip)
797 	painter.fillRect( event->rect(), Qt::black );
798 
799 	if ( !rogue_font ) {
800 	    // Find font...
801 	    int pts = 5;
802 	    QString fontfamily = iflags.wc_font_map
803 		? iflags.wc_font_map : "Courier";
804 	    bool bold = false;
805 	    if ( fontfamily.right(5).toLower() == "-bold" ) {
806 		fontfamily.truncate(fontfamily.length()-5);
807 		bold = true;
808 	    }
809 	    while ( pts < 32 ) {
810 		QFont f(fontfamily, pts, bold ? QFont::Bold : QFont::Normal);
811 		painter.setFont(QFont(fontfamily, pts));
812 		QFontMetrics fm = painter.fontMetrics();
813 		if ( fm.width("M") > qt_settings->glyphs().width() )
814 		    break;
815 		if ( fm.height() > qt_settings->glyphs().height() )
816 		    break;
817 		pts++;
818 	    }
819 	    rogue_font = new QFont(fontfamily,pts-1);
820 	}
821 	painter.setFont(*rogue_font);
822 
823 	for (int j=garea.top(); j<=garea.bottom(); j++) {
824 	    for (int i=garea.left(); i<=garea.right(); i++) {
825 		unsigned short g=Glyph(i,j);
826 		int color;
827 		char32_t ch;
828 		unsigned special;
829 
830 		painter.setPen( Qt::green );
831 		/* map glyph to character and color */
832     		mapglyph(g, &ch, &color, &special, i, j, 0);
833 #ifdef TEXTCOLOR
834 		painter.setPen( nhcolor_to_pen(color) );
835 #endif
836 		painter.drawText(
837 		    i*qt_settings->glyphs().width(),
838 		    j*qt_settings->glyphs().height(),
839 		    qt_settings->glyphs().width(),
840 		    qt_settings->glyphs().height(),
841 		    Qt::AlignCenter,
842 		    QString(QChar(ch)).left(1)
843 		);
844 #ifdef TEXTCOLOR
845 		if (((special & MG_PET) != 0) && ::iflags.hilite_pet) {
846                     painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation);
847                 } else if (((special & MG_OBJPILE) != 0) && ::iflags.hilite_pile) {
848                     painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pile_annotation);
849                 }
850 #endif
851 	    }
852 	}
853 
854 	painter.setFont(font());
855     } else {
856 	for (int j=garea.top(); j<=garea.bottom(); j++) {
857 	    for (int i=garea.left(); i<=garea.right(); i++) {
858 		unsigned short g=Glyph(i,j);
859 		int color;
860 		int ch;
861 		unsigned special;
862 		mapglyph(g, &ch, &color, &special, i, j, 0);
863 		qt_settings->glyphs().drawCell(painter, g, i, j);
864 #ifdef TEXTCOLOR
865 		if (((special & MG_PET) != 0) && ::iflags.hilite_pet) {
866                     painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation);
867                 } else if (((special & MG_OBJPILE) != 0) && ::iflags.hilite_pile) {
868                     painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pile_annotation);
869                 }
870 #endif
871 	    }
872 	}
873     }
874 
875     if (garea.contains(cursor)) {
876 	if (Is_rogue_level(&u.uz)) {
877 #ifdef TEXTCOLOR
878 	    painter.setPen( Qt::white );
879 #else
880 	    painter.setPen( Qt::green ); // REALLY primitive
881 #endif
882 	} else
883 	{
884 	    int hp100;
885 	    if (u.mtimedone) {
886 		hp100=u.mhmax ? u.mh*100/u.mhmax : 100;
887 	    } else {
888 		hp100=u.uhpmax ? u.uhp*100/u.uhpmax : 100;
889 	    }
890 
891 	    if (hp100 > 75) painter.setPen(Qt::white);
892 	    else if (hp100 > 50) painter.setPen(Qt::yellow);
893 	    else if (hp100 > 25) painter.setPen(QColor(0xff,0xbf,0x00)); // orange
894 	    else if (hp100 > 10) painter.setPen(Qt::red);
895 	    else painter.setPen(Qt::magenta);
896 	}
897 
898 	painter.drawRect(
899 	    cursor.x()*qt_settings->glyphs().width(),cursor.y()*qt_settings->glyphs().height(),
900 	    qt_settings->glyphs().width()-1,qt_settings->glyphs().height()-1);
901     }
902 
903     if (area.intersects(messages_rect)) {
904 	painter.setPen(Qt::black);
905 	painter.drawText(viewport.contentsX()+1,viewport.contentsY()+1,
906 	    viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages);
907 	painter.setPen(Qt::white);
908 	painter.drawText(viewport.contentsX(),viewport.contentsY(),
909 	    viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages);
910     }
911 
912     painter.end();
913 }
914 
915 void NetHackQtMapWindow::Display(bool block)
916 {
917     for (int i=0; i<change.clusters(); i++) {
918 	const QRect& ch=change[i];
919 	repaint(
920 	    ch.x()*qt_settings->glyphs().width(),
921 	    ch.y()*qt_settings->glyphs().height(),
922 	    ch.width()*qt_settings->glyphs().width(),
923 	    ch.height()*qt_settings->glyphs().height()
924 	);
925     }
926 
927     change.clear();
928 
929     if (block) {
930 	yn_function("Press a key when done viewing",0,'\0');
931     }
932 }
933 
934 void NetHackQtMapWindow::CursorTo(int x,int y)
935 {
936     Changed(cursor.x(),cursor.y());
937     cursor.setX(x);
938     cursor.setY(y);
939     Changed(cursor.x(),cursor.y());
940 }
941 
942 void NetHackQtMapWindow::PutStr(int attr, const QString& text)
943 {
944     puts("unexpected PutStr in MapWindow");
945 }
946 
947 void NetHackQtMapWindow::ClipAround(int x,int y)
948 {
949     // Convert to pixel of center of tile
950     x=x*qt_settings->glyphs().width()+qt_settings->glyphs().width()/2;
951     y=y*qt_settings->glyphs().height()+qt_settings->glyphs().height()/2;
952 
953     // Then ensure that pixel is visible
954     viewport.center(x,y,0.45,0.45);
955 }
956 
957 void NetHackQtMapWindow::PrintGlyph(int x,int y,int glyph)
958 {
959     Glyph(x,y)=glyph;
960     Changed(x,y);
961 }
962 
963 //void NetHackQtMapWindow::PrintGlyphCompose(int x,int y,int glyph1, int glyph2)
964 //{
965     // TODO: composed graphics
966 //}
967 
968 void NetHackQtMapWindow::Changed(int x, int y)
969 {
970     change.add(x,y);
971 }
972 #endif
973 
974 } // namespace nethack_qt4
975