📄 kquickhelp.cpp
字号:
// ---------------------------------------------------------------------------//// QuickHelp: an improved mini-help system for KDE//// ---------------------------------------------------------------------------//// This file is part of the KDE libraries//// Copyright (C) 1997 Mario Weilguni <mweilguni@kde.org>//// This library is free software; you can redistribute it and/or// modify it under the terms of the GNU Library General Public// License as published by the Free Software Foundation; either// version 2 of the License, or (at your option) any later version.//// This library is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU// Library General Public License for more details.//// You should have received a copy of the GNU Library General Public License// along with this library; see the file COPYING.LIB. If not, write to// the Free Software Foundation, Inc., 59 Temple Place - Suite 330,// Boston, MA 02111-1307, USA.//// ---------------------------------------------------------------------------#include "kquickhelp.h"#include <qpainter.h>#include <qpixmap.h>#include <stdio.h>#include <kapp.h>#include <qpopupmenu.h>#include <qbitmap.h>#include <qregexp.h>#ifdef HAVE_CONFIG_H#include <config.h>#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif// this selects wheter we want shapes or not#define QH_SHAPE#ifdef QH_SHAPE #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xos.h> #include <X11/extensions/shape.h> #define X_SHADOW 6 #define Y_SHADOW 6#else #define X_SHADOW 0 #define Y_SHADOW 0#endif// text attribute flags#define F_BOLD 1#define F_ITALIC 2#define F_UNDERLINE 4// predefined border and shadow width#define X_BORDER 10#define Y_BORDER 6// numeric representation of allowed tokensconst int TOK_BOLD_ON = 1;const int TOK_BOLD_OFF = 2;const int TOK_ITALIC_ON = 3;const int TOK_ITALIC_OFF = 4;const int TOK_UNDERLINE_ON = 5;const int TOK_UNDERLINE_OFF = 6;const int TOK_FONTSIZE_PLUS = 7;const int TOK_FONTSIZE_MINUS = 8;const int TOK_COLOR_BY_RGB = 9;const int TOK_COLOR_RED = 10;const int TOK_COLOR_GREEN = 11;const int TOK_COLOR_BLUE = 12;const int TOK_COLOR_WHITE = 13;const int TOK_COLOR_YELLOW = 14;const int TOK_COLOR_BLACK = 15;const int TOK_COLOR_BROWN = 16;const int TOK_COLOR_MAGENTA = 17;const int TOK_COLOR_CYAN = 18;const int TOK_FONT_DEFAULT = 19;const int TOK_FONT_FIXED = 20;const int TOK_INDENT_PLUS = 21;const int TOK_INDENT_MINUS = 22;const int TOK_NEWLINE = 23;const int TOK_LINK = 24;const int TOK_ENDLINK = 25;QPopupMenu *KQuickHelp::menu = 0;KQuickHelp *KQuickHelp::instance = 0;QList<KQuickTip> *KQuickHelp::tips;KQuickHelpWindow *KQuickHelp::window = 0;// the standard colorsQColor KQuickHelpWindow::stdColors[] = { red, green, blue, white, yellow, black, QColor(165, 42, 42), magenta, cyan};KQuickHelp_Token KQuickHelpWindow::tokens[] = { // font attributes {"<b>", TOK_BOLD_ON}, {"</b>", TOK_BOLD_OFF}, {"<i>", TOK_ITALIC_ON}, {"</i>", TOK_ITALIC_OFF}, {"<u>", TOK_UNDERLINE_ON}, {"</u>", TOK_UNDERLINE_OFF}, // font attributes (long) {"<bold>", TOK_BOLD_ON}, {"</bold>", TOK_BOLD_OFF}, {"<italic>", TOK_ITALIC_ON}, {"</italic>", TOK_ITALIC_OFF}, {"<underline>", TOK_UNDERLINE_ON}, {"</underline>", TOK_UNDERLINE_OFF}, // font size {"<FONT SIZE +>", TOK_FONTSIZE_PLUS}, {"<FONT SIZE ->", TOK_FONTSIZE_MINUS}, {"<+>", TOK_FONTSIZE_PLUS}, {"<->", TOK_FONTSIZE_MINUS}, // indentation {"<INDENT +>", TOK_INDENT_PLUS}, {"<INDENT ->", TOK_INDENT_MINUS}, {"<i+>", TOK_INDENT_PLUS}, {"<i->", TOK_INDENT_MINUS}, // color selection by RGB value {"<COLOR #", TOK_COLOR_BY_RGB}, // color selection by color name {"<COLOR RED>", TOK_COLOR_RED}, {"<COLOR GREEN>", TOK_COLOR_GREEN}, {"<COLOR BLUE>", TOK_COLOR_BLUE}, {"<COLOR WHITE>", TOK_COLOR_WHITE}, {"<COLOR YELLOW>", TOK_COLOR_YELLOW}, {"<COLOR BLACK>", TOK_COLOR_BLACK}, {"<COLOR BROWN>", TOK_COLOR_BROWN}, {"<COLOR MAGENTA>", TOK_COLOR_MAGENTA}, {"<COLOR CYAN>", TOK_COLOR_CYAN}, // color selection by color name (short form) {"<RED>", TOK_COLOR_RED}, {"<GREEN>", TOK_COLOR_GREEN}, {"<BLUE>", TOK_COLOR_BLUE}, {"<WHITE>", TOK_COLOR_WHITE}, {"<YELLOW>", TOK_COLOR_YELLOW}, {"<BLACK>", TOK_COLOR_BLACK}, {"<BROWN>", TOK_COLOR_BROWN}, {"<MAGENTA>", TOK_COLOR_MAGENTA}, {"<CYAN>", TOK_COLOR_CYAN}, // font selection {"<FONT DEFAULT>", TOK_FONT_DEFAULT}, {"<FONT FIXED>", TOK_FONT_FIXED}, {"<DEFAULT>", TOK_FONT_DEFAULT}, {"<FIXED>", TOK_FONT_FIXED}, // newline {"<br>", TOK_NEWLINE}, // hyperlinks {"<link ", TOK_LINK}, {"</link>", TOK_ENDLINK}, // token table terminator {0, 0}};KQuickHelp::KQuickHelp() : QObject(0) {}const char *KQuickHelp::add(QWidget *w, const char *s) { if(!s) return 0; if(w != 0) { // make sure we have a class instance running if(instance == 0) { tips = new QList<KQuickTip>; instance = new KQuickHelp(); window = new KQuickHelpWindow(); menu = new QPopupMenu; menu->insertItem(i18n("Quickhelp")); connect(menu, SIGNAL(activated(int)), instance, SLOT(getKQuickHelp(int))); connect(window, SIGNAL(hyperlink(QString)), instance, SLOT(hyperlinkRequested(QString))); tips->setAutoDelete(true); } KQuickTip *qt = new KQuickTip; qt->widget = w; qt->txt = s; tips->append(qt); connect(w, SIGNAL(destroyed()), instance, SLOT(widgetDestroyed())); w->installEventFilter(instance); } return s;}void KQuickHelp::hyperlinkRequested(QString link) { // check protocol if(strncasecmp("http:", link.data(), 5) == 0 || strncasecmp("info:", link.data(), 5) == 0 || strncasecmp("ftp:", link.data(), 4) == 0 || strncasecmp("file:", link.data(), 5) == 0 || strncasecmp("mailto:", link.data(), 7) == 0) { // lets give this URL to kfm, he knows better what // to do with it if(fork() > 0) { // drop setuid, setgid setgid(getgid()); setuid(getuid()); QString s = "kfmclient exec " + link + " &"; execlp("kfmclient", "kfmclient", "exec", link.data(), 0); _exit(0); } } else { // hmm, seems I have to do it myself :-( QString fname = "", anchor = ""; int idx; if((idx = link.find('#')) == -1) fname = link; else { if(*link.data() == '#') { anchor = link.mid(1, 255); fname = kapp->appName() + ".html"; } else { fname = link.left(idx); anchor = link.mid(idx+1, 1024); } } kapp->invokeHTMLHelp((kapp->appName() + "/" + fname), anchor); }}void KQuickHelp::remove(QWidget *w) { for(unsigned i = 0; i < tips->count(); i++) if(tips->at(i)->widget == w) { tips->remove(i); return; } }void KQuickHelp::widgetDestroyed() { remove((QWidget *)sender());}bool KQuickHelp::eventFilter(QObject *o, QEvent *e) { if(e->type() == Event_MouseButtonPress && ((QMouseEvent *)e)->button() == RightButton) { for(unsigned i = 0; i < tips->count(); i++) { if(tips->at(i)->widget == (QWidget *)o) { current = tips->at(i)->widget; currentText = tips->at(i)->txt; currentPos = QCursor::pos(); menu->popup(currentPos); return true; } } } return false;}void KQuickHelp::getKQuickHelp(int) { window->popup(currentText, currentPos.x(), currentPos.y());}KQuickHelpWindow::KQuickHelpWindow() : QFrame(0, 0, WStyle_Customize|WStyle_Tool) { setBackgroundColor(QColor(255, 255, 225)); defaultCursor = cursor(); linkAreas.setAutoDelete(true);}void KQuickHelpWindow::newText() { QPixmap pm(1, 1); QPainter p; p.begin(&pm); int w = 0, h = 0; paint(&p, w, h); p.end(); // HACK: there may have been link-areas defined. Remove them linkAreas.clear(); // resize to appropr. size resize(w, h);}void KQuickHelpWindow::mousePressEvent(QMouseEvent *e) { if(e->button() == LeftButton && activeLink.length()) emit hyperlink(activeLink); hide();}void KQuickHelpWindow::mouseMoveEvent(QMouseEvent *e) { QPoint p = e->pos(); for(uint i = 0; i < linkAreas.count(); i++) if(linkAreas.at(i)->area.contains(p)) { setCursor(upArrowCursor); activeLink = linkAreas.at(i)->link.copy(); return; } activeLink = ""; setCursor(defaultCursor);}void KQuickHelpWindow::keyPressEvent(QKeyEvent *e) { e->accept(); hide();}void KQuickHelpWindow::show() { QFrame::show(); grabMouse(); grabKeyboard(); setMouseTracking(true);}void KQuickHelpWindow::hide() { QFrame::hide(); releaseMouse(); releaseKeyboard(); setMouseTracking(false); // clean up activeLink = ""; linkAreas.clear();}void KQuickHelpWindow::popup(QString text, int x, int y) { txt = text.copy(); newText(); if(x + width() > QApplication::desktop()->width() - 8) x = QApplication::desktop()->width() - 8 - width(); if(y + height() > QApplication::desktop()->height() - 4) y = QApplication::desktop()->height() - 4 - height(); move(x, y); show();} void KQuickHelpWindow::paintEvent(QPaintEvent *) { QPainter p; p.begin(this); int x, y; paint(&p, x, y); p.end();#ifdef QH_SHAPE // repaint, so we can apply the Shape (don't know if necessary) // kapp->flushX(); QBitmap bm(width(), height()); p.begin(&bm); p.setPen(color0); bm.fill(color1); p.fillRect(X_SHADOW, height() - Y_SHADOW, width() - X_SHADOW, Y_SHADOW, Dense4Pattern); p.fillRect(width()-X_SHADOW, Y_SHADOW, X_SHADOW, height()-Y_SHADOW, Dense4Pattern); p.fillRect(width()-X_SHADOW, 0, X_SHADOW, Y_SHADOW, SolidPattern); p.fillRect(0, height()-Y_SHADOW, X_SHADOW, Y_SHADOW, SolidPattern); p.end(); XShapeCombineMask( x11Display(), winId(), ShapeBounding, 0, 0, bm.handle(), ShapeSet );#endif}QString KQuickHelpWindow::token() { QString t = ""; bool backslash = false; while(tokIndex < (int)txt.length()) { char c = txt[tokIndex++]; if(backslash) { backslash = false; t += c; } else { switch(c) { case '\\': backslash = true; break; case '\n': if(t.length() == 0) t = "<br>"; else tokIndex--; return t; case '<': if(t.length() == 0) t += c; else { tokIndex--; return t; } break; case '>': if(t.left(1) == "<") // bad token t += c; return t; default: t += c; } } } return t;}void KQuickHelpWindow::paint(QPainter *p, int &w, int &h) { int posx = X_BORDER, posy = Y_BORDER; int maxy = 0, maxx = 0, idx; int indent = 0; QFont f; QColor txtColor = black; QColor savedColor; int txtFlags = 0; bool wasUnderline = false; QString link; int pointsize = p->font().pointSize(); p->setPen(black); tokIndex = 0; while(tokIndex < (int)txt.length()) { QString t = token(); if(t.left(1) == "<") { // a token // find token int tokenID = -1; t = t.simplifyWhiteSpace(); for(int i = 0; tokens[i].token != 0; i++) if(strncasecmp(t.data(), tokens[i].token, strlen(tokens[i].token)) == 0) { tokenID = tokens[i].tokenID; break; } switch(tokenID) { case -1: // ignore fprintf(stderr, "KQuickHelp: ignoring unknown token \"%s\"!\n", t.data()); break; // font attributes case TOK_BOLD_ON: txtFlags |= F_BOLD; break; case TOK_BOLD_OFF: txtFlags &= ~F_BOLD; break; case TOK_ITALIC_ON: txtFlags |= F_ITALIC; break; case TOK_ITALIC_OFF: txtFlags &= ~F_ITALIC; break; case TOK_UNDERLINE_ON: txtFlags |= F_UNDERLINE; break; case TOK_UNDERLINE_OFF: txtFlags &= ~F_UNDERLINE; break; // font size case TOK_FONTSIZE_PLUS: f = p->font(); while(QFontInfo(f).pointSize() == pointsize) f.setPointSize(f.pointSize() + 1); pointsize = QFontInfo(f).pointSize(); break; case TOK_FONTSIZE_MINUS: f = p->font(); while(QFontInfo(f).pointSize() == pointsize) f.setPointSize(f.pointSize() - 1); pointsize = QFontInfo(f).pointSize(); break; // indentation case TOK_INDENT_PLUS: posx += fontMetrics().width('H') * 2; indent += fontMetrics().width('H') * 2; break; case TOK_INDENT_MINUS: if(indent > 0) indent -= fontMetrics().width('H') * 2; break; // color by RGB value case TOK_COLOR_BY_RGB: idx = t.find(QRegExp("#[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]")); if(idx != -1) { int cval, r, g, b; sscanf(t.data() + idx + 1, "%x", &cval); r = cval >> 16; g = (cval >> 8) & 0xff; b = cval & 0xff; txtColor = QColor(r, g, b); } break; // newline case TOK_NEWLINE: posx = X_BORDER + indent; posy += maxy; maxy = 10; break; // standard colors case TOK_COLOR_RED: case TOK_COLOR_GREEN: case TOK_COLOR_BLUE: case TOK_COLOR_WHITE: case TOK_COLOR_YELLOW: case TOK_COLOR_BLACK: case TOK_COLOR_BROWN: case TOK_COLOR_MAGENTA: case TOK_COLOR_CYAN: txtColor = stdColors[tokenID-TOK_COLOR_RED]; break; // hyperlinks case TOK_LINK: idx = t.find(QRegExp(" .*>", false)); if(idx != -1) { link = t.mid(idx+1, t.length()-idx-2); savedColor = txtColor; wasUnderline = (txtFlags & F_UNDERLINE) != 0; txtColor = blue; txtFlags |= F_UNDERLINE; } break; case TOK_ENDLINK: txtColor = savedColor; if(!wasUnderline) txtFlags &= ~F_UNDERLINE; link = ""; break; default: printf("TOKEN: \"%s\", ID=%d\n", t.data(), tokenID); } } else { // plain text p->setPen(txtColor); QFont f = font(); f.setPointSize(pointsize); f.setBold((txtFlags & F_BOLD) != 0); f.setItalic((txtFlags & F_ITALIC) != 0); f.setUnderline((txtFlags & F_UNDERLINE) != 0); p->setFont(f); QRect r; p->drawText(posx, posy, 1024, 1024, AlignLeft|AlignTop, t.data(), -1, &r, 0); if(link.length() > 0) { KQuickHelp_Link *l = new KQuickHelp_Link; l->area = r; l->link = link; linkAreas.append(l); } posx += r.width(); maxy = QMAX(maxy, p->fontMetrics().lineSpacing()); maxx = QMAX(maxx, r.right()); } } w = maxx + X_BORDER + X_SHADOW; h = posy + maxy + Y_BORDER + Y_SHADOW; // draw frame p->setPen(QPen(black, 1)); p->drawRect(0, 0, w - X_SHADOW, h - Y_SHADOW); p->fillRect(0, h - Y_SHADOW, w, h, QBrush(QColor(black))); p->fillRect(w - X_SHADOW, 0, w, h, QBrush(QColor(black)));}#include "kquickhelp.moc"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -