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 // qt4plsel.cpp -- player selector dialog
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 "qt4plsel.h"
25 #include "qt4plsel.moc"
26 #include "qt4bind.h"
27 #include "qt4glyph.h"
28 #include "qt4set.h"
29 #include "qt4str.h"
30
31 // Warwick prefers it this way...
32 #define QT_CHOOSE_RACE_FIRST
33
34 namespace nethack_qt4 {
35
36 // temporary
37 void centerOnMain( QWidget* w );
38 // end temporary
39
40 static const char nh_attribution[] = "<center><big>NetHack %1</big>"
41 "<br><small>by the NetHack DevTeam</small></center>";
42
43 class NhPSListViewItem : public QTableWidgetItem {
44 public:
NhPSListViewItem(QTableWidget * parent,const QString & name)45 NhPSListViewItem( QTableWidget* parent, const QString& name ) :
46 QTableWidgetItem(name)
47 {
48 }
49
setGlyph(int g)50 void setGlyph(int g)
51 {
52 NetHackQtGlyphs& glyphs = qt_settings->glyphs();
53 int gw = glyphs.width();
54 int gh = glyphs.height();
55 QPixmap pm(gw,gh);
56 QPainter p(&pm);
57 glyphs.drawGlyph(p, g, 0, 0);
58 p.end();
59 setIcon(QIcon(pm));
60 //RLC setHeight(std::max(pm.height()+1,height()));
61 }
62
63 #if 0 //RLC
64 void paintCell( QPainter *p, const QColorGroup &cg,
65 int column, int width, int alignment )
66 {
67 if ( isSelectable() ) {
68 QTableWidgetItem::paintCell( p, cg, column, width, alignment );
69 } else {
70 QColorGroup disabled(
71 cg.foreground().light(),
72 cg.button().light(),
73 cg.light(), cg.dark(), cg.mid(),
74 Qt::gray, cg.base() );
75 QTableWidgetItem::paintCell( p, disabled, column, width, alignment );
76 }
77 }
78 #endif
79 };
80
81 class NhPSListViewRole : public NhPSListViewItem {
82 public:
NhPSListViewRole(QTableWidget * parent,int id)83 NhPSListViewRole( QTableWidget* parent, int id ) :
84 NhPSListViewItem(parent,
85 #ifdef QT_CHOOSE_RACE_FIRST // Lowerize - looks better
86 QString(roles[id].name.m).toLower()
87 #else
88 roles[id].name.m
89 #endif
90 )
91 {
92 setGlyph(monnum_to_glyph(roles[id].malenum));
93 }
94 };
95
96 class NhPSListViewRace : public NhPSListViewItem {
97 public:
NhPSListViewRace(QTableWidget * parent,int id)98 NhPSListViewRace( QTableWidget* parent, int id ) :
99 NhPSListViewItem(parent,
100 #ifdef QT_CHOOSE_RACE_FIRST // Capitalize - looks better
101 str_titlecase(races[id].noun)
102 #else
103 races[id].noun
104 #endif
105 )
106 {
107 setGlyph(monnum_to_glyph(races[id].malenum));
108 }
109 };
110
111 class NhPSListView : public QTableWidget {
112 public:
NhPSListView(QWidget * parent)113 NhPSListView( QWidget* parent ) :
114 QTableWidget(parent)
115 {
116 setColumnCount(1);
117 verticalHeader()->hide();
118 #if QT_VERSION >= 0x050000
119 horizontalHeader()->setSectionsClickable(false);
120 #else
121 horizontalHeader()->setClickable(false);
122 #endif
123 }
124
sizePolicy() const125 QSizePolicy sizePolicy() const
126 {
127 return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
128 }
129
minimumSizeHint() const130 QSize minimumSizeHint() const
131 {
132 return sizeHint();
133 }
134
sizeHint() const135 QSize sizeHint() const
136 {
137 return QSize(columnWidth(0), QTableWidget::sizeHint().height());
138 }
139 };
140
NetHackQtPlayerSelector(NetHackQtKeyBuffer & ks)141 NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) :
142 QDialog(NetHackQtBind::mainWidget()),
143 fully_specified_role(true)
144 {
145 /*
146 0 1 2
147 + Name ------------------------------------+
148 0 | |
149 + ---- ------------------------------------+
150 + Role ---+ + Race ---+ + Gender ------+
151 | | | | | * Male |
152 1 | | | | | * Female |
153 | | | | +--------------+
154 | | | |
155 | | | | + Alignment ---+
156 2 | | | | | * Male |
157 | | | | | * Female |
158 | | | | +--------------+
159 3 | | | | ...stretch...
160 | | | |
161 4 | | | | [ Random ]
162 5 | | | | [ Play ]
163 6 | | | | [ Quit ]
164 +---------+ +---------+
165 */
166
167 QGridLayout *l = new QGridLayout(this);
168 l->setColumnStretch(2, 1);
169 sizePolicy().setHorizontalPolicy(QSizePolicy::Minimum);
170
171 QGroupBox* namebox = new QGroupBox("Name", this);
172 QVBoxLayout *namelayout = new QVBoxLayout(namebox);
173 QLineEdit* name = new QLineEdit(namebox);
174 namelayout->addWidget(name);
175 name->setMaxLength(sizeof(plname)-1);
176 if ( strncmp(plname,"player",6) && strncmp(plname,"games",5) )
177 name->setText(plname);
178 connect(name, SIGNAL(textChanged(const QString&)),
179 this, SLOT(selectName(const QString&)) );
180 name->setFocus();
181 QGroupBox* genderbox = new QGroupBox("Gender",this);
182 QButtonGroup *gendergroup = new QButtonGroup(this);
183 QGroupBox* alignbox = new QGroupBox("Alignment",this);
184 QButtonGroup *aligngroup = new QButtonGroup(this);
185 QVBoxLayout* vbgb = new QVBoxLayout(genderbox);
186 QVBoxLayout* vbab = new QVBoxLayout(alignbox);
187 char versionbuf[QBUFSZ];
188 QLabel* logo = new QLabel(QString(nh_attribution).arg(version_string(versionbuf)), this);
189
190 l->addWidget( namebox, 0,0,1,3 );
191 #ifdef QT_CHOOSE_RACE_FIRST
192 race = new NhPSListView(this);
193 role = new NhPSListView(this);
194 l->addWidget( race, 1,0,6,1 );
195 l->addWidget( role, 1,1,6,1 );
196 #else
197 role = new NhPSListView(this);
198 race = new NhPSListView(this);
199 l->addWidget( role, 1,0,6,1 );
200 l->addWidget( race, 1,1,6,1 );
201 #endif
202
203 l->addWidget( genderbox, 1, 2 );
204 l->addWidget( alignbox, 2, 2 );
205 l->addWidget( logo, 3, 2, Qt::AlignCenter );
206 l->setRowStretch( 3, 6 );
207
208 int i;
209 int nrole;
210
211 chosen_gend = flags.initgend;
212 chosen_align = flags.initalign;
213
214 // XXX QListView unsorted goes in rev.
215 for (nrole=0; roles[nrole].name.m; nrole++)
216 ;
217 role->setRowCount(nrole);
218 for (i=0; roles[i].name.m; i++) {
219 QTableWidgetItem *item = new QTableWidgetItem(
220 QIcon(qt_settings->glyphs().glyph(roles[i].malenum)),
221 roles[i].name.m);
222 item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
223 role->setItem(i, 0, item);
224 }
225 connect( role, SIGNAL(currentCellChanged(int, int, int, int)), this, SLOT(selectRole(int, int, int, int)) );
226 role->setHorizontalHeaderLabels(QStringList("Role"));
227 role->resizeColumnToContents(0);
228
229 int nrace;
230 for (nrace=0; races[nrace].noun; nrace++)
231 ;
232 race->setRowCount(nrace);
233 for (i=0; races[i].noun; i++) {
234 QTableWidgetItem *item = new QTableWidgetItem(
235 QIcon(qt_settings->glyphs().glyph(races[i].malenum)),
236 races[i].noun);
237 item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
238 race->setItem(i, 0, item);
239 }
240 connect( race, SIGNAL(currentCellChanged(int, int, int, int)), this, SLOT(selectRace(int, int, int, int)) );
241 race->setHorizontalHeaderLabels(QStringList("Race"));
242 race->resizeColumnToContents(0);
243
244 gender = new QRadioButton*[ROLE_GENDERS];
245 for (i=0; i<ROLE_GENDERS; i++) {
246 gender[i] = new QRadioButton( genders[i].adj, genderbox );
247 genderbox->layout()->addWidget(gender[i]);
248 gendergroup->addButton(gender[i], i);
249 }
250 connect( gendergroup, SIGNAL(buttonPressed(int)), this, SLOT(selectGender(int)) );
251
252 alignment = new QRadioButton*[ROLE_ALIGNS];
253 for (i=0; i<ROLE_ALIGNS; i++) {
254 alignment[i] = new QRadioButton( aligns[i].adj, alignbox );
255 alignbox->layout()->addWidget(alignment[i]);
256 aligngroup->addButton(alignment[i], i);
257 }
258 connect( aligngroup, SIGNAL(buttonPressed(int)), this, SLOT(selectAlignment(int)) );
259
260 QPushButton* rnd = new QPushButton("Random",this);
261 l->addWidget( rnd, 4, 2 );
262 rnd->setDefault(false);
263 connect( rnd, SIGNAL(clicked()), this, SLOT(Randomize()) );
264
265 QPushButton* ok = new QPushButton("Play",this);
266 l->addWidget( ok, 5, 2 );
267 ok->setDefault(true);
268 connect( ok, SIGNAL(clicked()), this, SLOT(accept()) );
269
270 QPushButton* cancel = new QPushButton("Quit",this);
271 l->addWidget( cancel, 6, 2 );
272 connect( cancel, SIGNAL(clicked()), this, SLOT(reject()) );
273
274 Randomize();
275 }
276
Randomize()277 void NetHackQtPlayerSelector::Randomize()
278 {
279 int nrole = role->rowCount();
280 int nrace = race->rowCount();
281
282 boolean picksomething = (flags.initrole == ROLE_NONE
283 || flags.initrace == ROLE_NONE
284 || flags.initgend == ROLE_NONE
285 || flags.initalign == ROLE_NONE);
286
287 if (flags.randomall && picksomething) {
288 if (flags.initrole == ROLE_NONE)
289 flags.initrole = ROLE_RANDOM;
290 if (flags.initrace == ROLE_NONE)
291 flags.initrace = ROLE_RANDOM;
292 if (flags.initgend == ROLE_NONE)
293 flags.initgend = ROLE_RANDOM;
294 if (flags.initalign == ROLE_NONE)
295 flags.initalign = ROLE_RANDOM;
296 }
297
298 rigid_role_checks();
299
300 // Randomize race and role, unless specified in config
301 int ro = flags.initrole;
302 if (ro == ROLE_NONE || ro == ROLE_RANDOM) {
303 ro = rn2(nrole);
304 if (flags.initrole != ROLE_RANDOM) {
305 fully_specified_role = false;
306 }
307 }
308 int ra = flags.initrace;
309 if (ra == ROLE_NONE || ra == ROLE_RANDOM) {
310 ra = rn2(nrace);
311 if (flags.initrace != ROLE_RANDOM) {
312 fully_specified_role = false;
313 }
314 }
315
316 // make sure we have a valid combination, honoring
317 // the users request if possible.
318 bool choose_race_first;
319 #ifdef QT_CHOOSE_RACE_FIRST
320 choose_race_first = true;
321 if (flags.initrole >= 0 && flags.initrace < 0) {
322 choose_race_first = false;
323 }
324 #else
325 choose_race_first = false;
326 if (flags.initrace >= 0 && flags.initrole < 0) {
327 choose_race_first = true;
328 }
329 #endif
330 while (!validrace(ro,ra)) {
331 if (choose_race_first) {
332 ro = rn2(nrole);
333 if (flags.initrole != ROLE_RANDOM) {
334 fully_specified_role = false;
335 }
336 } else {
337 ra = rn2(nrace);
338 if (flags.initrace != ROLE_RANDOM) {
339 fully_specified_role = false;
340 }
341 }
342 }
343
344 int g = flags.initgend;
345 if (g == -1) {
346 g = rn2(ROLE_GENDERS);
347 fully_specified_role = false;
348 }
349 while (!validgend(ro,ra,g)) {
350 g = rn2(ROLE_GENDERS);
351 }
352 gender[g]->setChecked(true);
353 selectGender(g);
354
355 int a = flags.initalign;
356 if (a == -1) {
357 a = rn2(ROLE_ALIGNS);
358 fully_specified_role = false;
359 }
360 while (!validalign(ro,ra,a)) {
361 a = rn2(ROLE_ALIGNS);
362 }
363 alignment[a]->setChecked(true);
364 selectAlignment(a);
365
366 role->setCurrentCell(ro, 0);
367
368 race->setCurrentCell(ra, 0);
369 }
370
selectName(const QString & n)371 void NetHackQtPlayerSelector::selectName(const QString& n)
372 {
373 str_copy(plname,n.toLatin1().constData(),SIZE(plname));
374 }
375
selectRole(int crow,int ccol,int prow,int pcol)376 void NetHackQtPlayerSelector::selectRole(int crow, int ccol, int prow, int pcol)
377 {
378 int ra = race->currentRow();
379 int ro = role->currentRow();
380 if (ra == -1 || ro == -1) return;
381 QTableWidgetItem* item;
382 item = role->item(prow, 0);
383 if (item != NULL)
384 item->setSelected(false);
385
386 #ifdef QT_CHOOSE_RACE_FIRST
387 selectRace(crow, ccol, prow, pcol);
388 #else
389 QTableWidgetItem* i=role->currentItem();
390 QTableWidgetItem* valid=0;
391 int j;
392 for (j=0; roles[j].name.m; j++) {
393 bool v = validrace(j,ra);
394 item = role->item(j, 0);
395 item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
396 if ( !valid && v ) valid = item;
397 }
398 if ( !validrace(role->currentRow(),ra) )
399 i = valid;
400 role->setCurrentItem(i, 0);
401 for (j=0; roles[j].name.m; j++) {
402 item = role->item(j, 0);
403 item->setSelected(item == i);
404 bool v = validrace(j,ra);
405 item->setFlags(
406 v ? Qt::ItemIsEnabled|Qt::ItemIsSelectable
407 : Qt::NoItemFlags);
408 }
409 #endif
410
411 //flags.initrole = role->currentRow();
412 setupOthers();
413 }
414
selectRace(int crow,int ccol,int prow,int pcol)415 void NetHackQtPlayerSelector::selectRace(int crow, int ccol, int prow, int pcol)
416 {
417 int ra = race->currentRow();
418 int ro = role->currentRow();
419 if (ra == -1 || ro == -1) return;
420 QTableWidgetItem* item;
421 item = race->item(prow, 0);
422 if (item != NULL)
423 item->setSelected(false);
424
425 #ifndef QT_CHOOSE_RACE_FIRST
426 selectRole(crow, ccol, prow, pcol);
427 #else
428 QTableWidgetItem* i=race->currentItem();
429 QTableWidgetItem* valid=0;
430 int j;
431 for (j=0; races[j].noun; j++) {
432 bool v = validrace(ro,j);
433 item = race->item(j, 0);
434 item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
435 if ( !valid && v ) valid = item;
436 }
437 if ( !validrace(ro,race->currentRow()) )
438 i = valid;
439 for (j=0; races[j].noun; j++) {
440 item = race->item(j, 0);
441 item->setSelected(item == i);
442 bool v = validrace(ro,j);
443 item->setFlags(
444 v ? Qt::ItemIsEnabled|Qt::ItemIsSelectable
445 : Qt::NoItemFlags);
446 }
447 #endif
448
449 //flags.initrace = race->currentRow();
450 setupOthers();
451 }
452
setupOthers()453 void NetHackQtPlayerSelector::setupOthers()
454 {
455 int ro = role->currentRow();
456 int ra = race->currentRow();
457 int valid=-1;
458 int c=0;
459 int j;
460 for (j=0; j<ROLE_GENDERS; j++) {
461 bool v = validgend(ro,ra,j);
462 if ( gender[j]->isChecked() )
463 c = j;
464 gender[j]->setEnabled(v);
465 if ( valid<0 && v ) valid = j;
466 }
467 if ( !validgend(ro,ra,c) )
468 c = valid;
469 int k;
470 for (k=0; k<ROLE_GENDERS; k++) {
471 gender[k]->setChecked(c==k);
472 }
473 selectGender(c);
474
475 valid=-1;
476 for (j=0; j<ROLE_ALIGNS; j++) {
477 bool v = validalign(ro,ra,j);
478 if ( alignment[j]->isChecked() )
479 c = j;
480 alignment[j]->setEnabled(v);
481 if ( valid<0 && v ) valid = j;
482 }
483 if ( !validalign(ro,ra,c) )
484 c = valid;
485 for (k=0; k<ROLE_ALIGNS; k++) {
486 alignment[k]->setChecked(c==k);
487 }
488 selectAlignment(c);
489 }
490
selectGender(int i)491 void NetHackQtPlayerSelector::selectGender(int i)
492 {
493 chosen_gend = i;
494 }
495
selectAlignment(int i)496 void NetHackQtPlayerSelector::selectAlignment(int i)
497 {
498 chosen_align = i;
499 }
500
Quit()501 void NetHackQtPlayerSelector::Quit()
502 {
503 done(R_Quit);
504 }
505
Random()506 void NetHackQtPlayerSelector::Random()
507 {
508 done(R_Rand);
509 }
510
Choose()511 bool NetHackQtPlayerSelector::Choose()
512 {
513 if (fully_specified_role) return true;
514
515 #if defined(QWS) // probably safe with Qt 3, too (where show!=exec in QDialog).
516 if ( qt_compact_mode ) {
517 showMaximized();
518 } else
519 #endif
520 {
521 adjustSize();
522 centerOnMain(this);
523 }
524
525 if ( exec() ) {
526 flags.initrace = race->currentRow();
527 flags.initrole = role->currentRow();
528 flags.initgend = chosen_gend;
529 flags.initalign = chosen_align;
530 return true;
531 } else {
532 return false;
533 }
534 }
535
536 } // namespace nethack_qt4
537