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