void centerOnMain( QWidget* w );
// end temporary
+static uchar keyValue(QKeyEvent *key_event)
+{
+ // key_event manipulation derived from NetHackQtBind::notify()
+ const int k = key_event->key();
+ Qt::KeyboardModifiers mod = key_event->modifiers();
+ QChar ch = !key_event->text().isEmpty() ? key_event->text().at(0) : 0;
+ if (ch >= 128)
+ ch = 0;
+ // on OSX, ascii control codes are not sent, force them
+ if (ch == 0 && (mod & Qt::ControlModifier) != 0) {
+ if (k >= Qt::Key_A && k <= Qt::Key_Underscore)
+ ch = QChar((k - (Qt::Key_A - 1)));
+ }
+ uchar result = (uchar) ch.cell();
+ //raw_printf("kV: k=%d, ch=%d", k, result);
+ return result;
+}
+
QSize NetHackQtTextListBox::sizeHint() const
{
QScrollBar *hscroll = horizontalScrollBar();
void NetHackQtMenuWindow::keyPressEvent(QKeyEvent *key_event)
{
- // key_event manipulation derived from NetHackQtBind::notify()
- const int k = key_event->key();
- Qt::KeyboardModifiers mod = key_event->modifiers();
- QChar ch = !key_event->text().isEmpty() ? key_event->text().at(0) : 0;
- if (ch > 128)
- ch = 0;
- // on OSX, ascii control codes are not sent, force them
- if (ch == 0 && (mod & Qt::ControlModifier) != 0) {
- if (k >= Qt::Key_A && k <= Qt::Key_Underscore)
- ch = (QChar) (k - (Qt::Key_A - 1));
- }
- uchar key = (char) ch.cell();
+ uchar key = keyValue(key_event);
+ if (!key)
+ return;
// only one possible match for key==ch, and if one occurs it takes
// precedence over any other match (for instance, some menus might
line[0] = '\0'; /* for EDIT_GETLIN */
if (requestor.Get(line)) {
for (int i=0; i<itemcount; i++) {
- if (itemlist[i].str.contains(line))
+ if (itemlist[i].str.contains(line, Qt::CaseInsensitive))
ToggleSelect(i, false);
}
}
return cstr.isEmpty() ? -1L : cstr.toLong();
}
+// initialize a text window
NetHackQtTextWindow::NetHackQtTextWindow(QWidget *parent) :
QDialog(parent),
use_rip(false),
str_fixed(false),
- ok("Dismiss",this),
- search("Search",this),
+ textsearching(false),
+ ok("&Dismiss", this),
+ search("&Search", this),
lines(new NetHackQtTextListBox(this)),
+ target(""),
rip(this)
{
+ //
+ // TODO?
+ // Searching would be far more convenient if the window contained
+ // the search string requestor widget instead of just a [Search]
+ // button to request a popup for that.
+ // Also, searching should probably be disabled if the entire text
+ // fits within the window so there's nothing to scroll through.
+ //
+
ok.setDefault(true);
- connect(&ok,SIGNAL(clicked()),this,SLOT(accept()));
- connect(&search,SIGNAL(clicked()),this,SLOT(Search()));
- connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate()));
+ connect(&ok,SIGNAL(clicked()), this, SLOT(doDismiss()));
+ connect(&search, SIGNAL(clicked()), this, SLOT(Search()));
+ connect(qt_settings, SIGNAL(fontChanged()), this, SLOT(doUpdate()));
QVBoxLayout* vb = new QVBoxLayout(this);
vb->addWidget(&rip);
hb->addWidget(&ok);
hb->addWidget(&search);
vb->addWidget(lines);
+
+ // we don't want keystrokes being sent to the main window for use as
+ // commands while this text window is popped up
+ setFocusPolicy(Qt::StrongFocus);
}
void NetHackQtTextWindow::doUpdate()
NetHackQtTextWindow::~NetHackQtTextWindow()
{
-
}
QWidget* NetHackQtTextWindow::Widget()
bool NetHackQtTextWindow::Destroy()
{
- return !isVisible();
+ return true; /*!isVisible();*/
+}
+
+void NetHackQtTextWindow::doDismiss()
+{
+ // [Clear() was needed when the search target string was kept in
+ // a static buffer but is superfluous now that that's part of
+ // the TextWindow class and initialized in the constructor.]
+ Clear();
+ accept();
}
void NetHackQtTextWindow::UseRIP(int how, time_t when)
void NetHackQtTextWindow::Clear()
{
lines->clear();
- use_rip=false;
- str_fixed=false;
+ target[0] = '\0'; // discard search target string
+ use_rip = false;
+ str_fixed = false;
+ textsearching = false;
}
void NetHackQtTextWindow::Display(bool block UNUSED)
{
+ // make sure window isn't completely empty
+ if (!lines->count())
+ PutStr(ATR_NONE, "");
+
if (str_fixed) {
lines->setFont(qt_settings->normalFixedFont());
} else {
centerOnMain(this);
show();
}
+
+ lines->clearSelection(); // affects [Search]
+
exec();
+ textsearching = false;
}
void NetHackQtTextWindow::PutStr(int attr UNUSED, const QString& text)
lines->addItem(text);
}
+// prompt for a target string and search current text window for it;
+// if found, highlight the next line target occurs on;
+// multiple searches with same or different search string are supported
void NetHackQtTextWindow::Search()
{
- NetHackQtStringRequestor requestor(this, "Search for:");
- static char line[256]="";
- requestor.SetDefault(line);
- if (requestor.Get(line)) {
- int current=lines->currentRow();
- for (int i=1; i<lines->count(); i++) {
- int lnum=(i+current)%lines->count();
- QString str=lines->item(lnum)->text();
- if (str.contains(line)) {
- lines->setCurrentRow(lnum);
- return;
- }
- }
- lines->setCurrentItem(NULL);
+ textsearching = true;
+ NetHackQtStringRequestor requestor(this, "Search for:", "Done", "Find");
+ requestor.SetDefault(target);
+ boolean get_a_line = requestor.Get(target, (int) sizeof target);
+
+ // FIXME:
+ // Force text window to be on top. Without this, it moves behind
+ // the map after the string requestor completes. Then it can't
+ // be seen or accessed (unless the game window is minimized or
+ // possibly dragged out of the way). Unfortunately the window
+ // noticeably vanishes and then immediately gets redrawn.
+ if (!this->isActiveWindow())
+ this->activateWindow();
+ this->raise();
+
+ if (get_a_line) {
+ int linecount = lines->count();
+ int current = lines->currentRow();
+ // when no row is highlighted (selected), start the search
+ // on the current row, otherwise start on the row after it
+ // [normally means that the very first row is a candidate
+ // for containgin the target during the very first search]
+ int startln = lines->selectedItems().count();
+ for (int i = startln; i < linecount; ++i) {
+ int lnum = (i + current) % linecount;
+ const QString &str = lines->item(lnum)->text();
+ // Check whether target occurs on this line. If it does,
+ // the line is highlighted and this search finishes.
+ // When not currently within view, highlighting also
+ // scrolls the view to make it become the bottom line.
+ // A subsequent search will remember the target string
+ // and start searching on the line past the highlighted
+ // one (even if a new target is specified).
+ if (str.contains(target, Qt::CaseInsensitive)) {
+ lines->setCurrentRow(lnum);
+ return;
+ }
+ }
+ lines->setCurrentItem(NULL);
+ } else {
+ target[0] = '\0';
+ }
+ textsearching = false;
+ return;
+}
+
+void NetHackQtTextWindow::keyPressEvent(QKeyEvent *key_event)
+{
+ uchar key = keyValue(key_event);
+
+ //
+ // FIXME:
+ // Typing ':' doesn't produce ':' so key won't match MENU_SEARCH,
+ // despite the fact that it does produce ':' for menu input and
+ // we're calling the exact same code for both types of window...?
+ //
+ if (key == MENU_SEARCH) {
+ if (!use_rip)
+ Search();
+ } else if (key == '\n' || key == '\r') {
+ if (!textsearching)
+ accept();
+ } else if (key == '\033') {
+ reject();
+ } else {
+ QDialog::keyPressEvent(key_event);
}
}