File indexing completed on 2024-03-24 15:43:41

0001 /***************************************************************************
0002                           cconsole.cpp  -  main displaying widget v2
0003     This file is a part of KMuddy distribution.
0004                              -------------------
0005     begin                : So Dec 21 2017
0006     copyright            : (C) 2002-2017 by Tomas Mecir
0007     email                : mecirt@gmail.com
0008  ***************************************************************************/
0009 
0010 /***************************************************************************
0011  *                                                                         *
0012  *   This program is free software; you can redistribute it and/or modify  *
0013  *   it under the terms of the GNU General Public License as published by  *
0014  *   the Free Software Foundation; either version 2 of the License, or     *
0015  *   (at your option) any later version.                                   *
0016  *                                                                         *
0017  ***************************************************************************/
0018 
0019 #include "cconsole.h"
0020 
0021 #include "cactionmanager.h"
0022 #include "ctextchunk.h"
0023 
0024 #include <QAbstractTextDocumentLayout>
0025 #include <QApplication>
0026 #include <QAction>
0027 #include <QDesktopServices>
0028 #include <QGraphicsItemGroup>
0029 #include <QGraphicsSceneHelpEvent>
0030 #include <QGraphicsTextItem>
0031 #include <QScrollBar>
0032 #include <QTextBlock>
0033 #include <QTextBlockFormat>
0034 #include <QTextCursor>
0035 #include <QTextDocument>
0036 #include <QToolTip>
0037 
0038 #include <KActionCollection>
0039 #include <KLocalizedString>
0040 
0041 
0042 class cTextOutputItem : public QGraphicsTextItem {
0043 public:
0044   cTextOutputItem(bool sec) {
0045     isSecondary = sec;
0046     bgcolor = Qt::black;
0047   }
0048 
0049   void setBackgroundColor (QColor color) {
0050     bgcolor = color;
0051   }
0052 
0053   QPainterPath opaqueArea() const override {
0054     return shape();
0055   }
0056 
0057   void paint (QPainter *painter, const QStyleOptionGraphicsItem *o, QWidget *w) override {
0058     painter->setBrush (bgcolor);
0059     painter->drawRect (boundingRect());
0060     QGraphicsTextItem::paint (painter, o, w);
0061   }
0062 
0063   QRectF boundingRect() const override {
0064     QRectF rect = scene()->sceneRect();
0065     // add +1 to each side to eliminate the selection box, which we do not want
0066     return QRectF (-1, -1, rect.width() + 2, rect.height() + 2);
0067   }
0068 
0069   void updateSize () {
0070     QRectF rect = scene()->sceneRect();
0071     double w = rect.width();
0072     if (textWidth() == w) return;  // don't update the width if it already is fine - it's an expensive operation
0073     setTextWidth (w);
0074     prepareGeometryChange();
0075   }
0076 
0077 protected:
0078   bool isSecondary;
0079   QColor bgcolor;
0080 };
0081 
0082 class cScrollTextGroup : public QGraphicsItemGroup
0083 {
0084 public:
0085   cScrollTextGroup() {
0086     _percentHeight = 25;
0087     setFlag (QGraphicsItem::ItemClipsChildrenToShape);
0088   }
0089 
0090   QRectF boundingRect() const override {
0091     QGraphicsScene *sc = scene();
0092     if (sc->views().isEmpty()) return QRectF (0, 0, 0, 0);
0093     QGraphicsView *view = sc->views().first();
0094     double w = view->viewport()->width();
0095     double h = view->viewport()->height();
0096     h = h * _percentHeight / 100;
0097     return QRectF (0, 0, w, h);
0098   }
0099 
0100   void setPercentHeight (int ph) {
0101     updateSize();
0102     _percentHeight = ph;
0103   }
0104 
0105   int percentHeight () {
0106     return _percentHeight;
0107   }
0108 
0109   void updateSize () {
0110     prepareGeometryChange();
0111   }
0112 
0113 protected:
0114   int _percentHeight;
0115 };
0116 
0117 class cConsoleTimestamp : public QTextBlockUserData {
0118  public:
0119   cConsoleTimestamp(QDateTime s) { stamp = s; }
0120   ~cConsoleTimestamp() override{};
0121   QDateTime stamp;
0122 };
0123 
0124 class cConsoleScene : public QGraphicsScene {
0125 
0126  protected:
0127 
0128   QString formatTimeStamp (QDateTime timestamp) {
0129     QString stamp = timestamp.toString ("hh:mm:ss");
0130     
0131     int secsago = timestamp.secsTo (QDateTime::currentDateTime());
0132     if (secsago == 0)  //special case: NOW!
0133       stamp += (" (" + i18n ("now") + ")");
0134     else
0135     {
0136       int minsago = secsago / 60;
0137       int hoursago = minsago / 60;
0138       secsago = secsago % 60;
0139       minsago = minsago % 60;
0140       // FIXME: fix word puzzle
0141       stamp += " (";
0142       if (hoursago)
0143         stamp += QString::number (hoursago) + ((hoursago == 1) ? i18n ("hour") : i18n ("hours"));
0144       if (minsago && (hoursago < 10))  //don't show minutes if >= 10 hrs
0145       {
0146         if (hoursago)
0147           stamp += " ";
0148         stamp += QString::number (minsago) + " " + ((minsago == 1) ? i18n ("minute") : i18n ("minutes"));
0149       }
0150       if (secsago && (!(hoursago || (minsago >= 5))))  //don't show seconds if >= 5 mins ago
0151       {
0152         if (minsago || hoursago)
0153           stamp += " ";
0154         stamp += QString::number (secsago) + " " + ((secsago == 1) ? i18n ("second") : i18n ("seconds"));
0155       }
0156       stamp += (" " + i18n ("ago") + ")");
0157     }
0158     return stamp;
0159   }
0160 
0161   void helpEvent(QGraphicsSceneHelpEvent *helpEvent) override {
0162     QGraphicsItem *top = itemAt (helpEvent->scenePos(), QTransform());
0163     if (!top) {
0164       QToolTip::hideText();
0165       return;
0166     }
0167     cTextOutputItem *text = dynamic_cast<cTextOutputItem *>(top);
0168     if (!text) {
0169       QToolTip::hideText();
0170       return;   // not a text item
0171     }
0172 
0173     // get the block at the mouse position
0174     cConsoleTimestamp *stamp = nullptr;
0175     QPointF coord = text->mapFromScene (helpEvent->scenePos());
0176     QAbstractTextDocumentLayout *layout = text->document()->documentLayout();
0177     QTextBlock block = text->document()->lastBlock();
0178     while (block != text->document()->firstBlock()) {
0179       if (layout->blockBoundingRect(block).contains (coord)) {  // found it
0180         stamp = dynamic_cast<cConsoleTimestamp *>(block.userData());
0181         break;
0182       }
0183       block = block.previous();
0184     }
0185 
0186     if (!stamp) {
0187       QToolTip::hideText();
0188       return;   // not in text
0189     }
0190 
0191     QString tip = formatTimeStamp (stamp->stamp);
0192     QToolTip::showText(helpEvent->screenPos(), tip);
0193   }
0194 
0195 };
0196 
0197 
0198 class cConsole::Private {
0199   cConsoleScene scene;
0200   cTextOutputItem *mainText, *scrollText;
0201   cScrollTextGroup *scrollTextGroup;
0202   QTextDocument *text;
0203 
0204   int historySize;
0205   QColor bgcolor;
0206   QFont font;
0207   int sess;
0208   int indentChars;
0209   double charWidth, charHeight;
0210   bool wantNewLine;
0211   bool atBottom;
0212 
0213   friend class cConsole;
0214 };
0215 
0216 
0217 
0218 cConsole::cConsole(QWidget *parent) : QGraphicsView(parent) {
0219   d = new Private;
0220   d->sess = -1;
0221   d->charWidth = 12;
0222   d->charHeight = 12;
0223   d->wantNewLine = false;
0224   d->atBottom = true;
0225   d->historySize = 1000;
0226   d->indentChars = 0;
0227 
0228   setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
0229   setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
0230 
0231   //and connect() slider so that aconsole is shown/hidden as needed
0232   connect (verticalScrollBar (), &QScrollBar::sliderMoved, this, &cConsole::sliderChanged);
0233   connect (verticalScrollBar (), &QScrollBar::valueChanged, this, &cConsole::sliderChanged);
0234     
0235   d->text = new QTextDocument;
0236   d->text->setUndoRedoEnabled (false);
0237 //  QString stylesheet = "* { white-space: pre-wrap; } a { color: " + QColor (Qt::blue).name() + "; }  ";
0238 //  d->text->setDefaultStyleSheet (stylesheet);
0239   QTextOption opt;
0240   opt.setWrapMode (QTextOption::WrapAtWordBoundaryOrAnywhere);
0241   d->text->setDefaultTextOption (opt);
0242 
0243   //size policy
0244   QSizePolicy qsp (QSizePolicy::Expanding, QSizePolicy::Expanding);
0245   setSizePolicy (qsp);
0246   setFocusPolicy (Qt::NoFocus);
0247 
0248   // scene
0249   setScene (&d->scene);
0250   // this needs to be set immediately to prevent an endless recursion
0251   scene()->setSceneRect (0, 0, viewport()->width(), viewport()->height());
0252 
0253   d->mainText = new cTextOutputItem (false);
0254   d->scrollText = new cTextOutputItem (true);
0255   d->scrollTextGroup = new cScrollTextGroup;
0256   d->scene.addItem (d->mainText);
0257   d->scene.addItem (d->scrollTextGroup);
0258   d->scene.addItem (d->scrollText);
0259   connect (d->mainText, &QGraphicsTextItem::linkActivated, this, &cConsole::linkActivated);
0260   connect (d->scrollText, &QGraphicsTextItem::linkActivated, this, &cConsole::linkActivated);
0261   connect (d->mainText, &QGraphicsTextItem::linkHovered, this, &cConsole::linkHovered);
0262   connect (d->scrollText, &QGraphicsTextItem::linkHovered, this, &cConsole::linkHovered);
0263 
0264   d->mainText->setDocument (d->text);
0265 //  d->mainText->setFiltersChildEvents (true);
0266   d->mainText->setPos (0, 0);
0267   d->mainText->setTextInteractionFlags (Qt::TextBrowserInteraction);
0268 
0269   d->scrollTextGroup->setParentItem (d->mainText);
0270   d->scrollTextGroup->setVisible (false);
0271 
0272   d->scrollText->setDocument (d->text);
0273   d->scrollText->setParentItem (d->scrollTextGroup);
0274   d->scrollText->setPos (0, 0);
0275   d->scrollText->setTextInteractionFlags (Qt::TextBrowserInteraction);
0276 
0277   d->scene.setFocusItem (d->mainText);
0278   connect (&d->scene, &QGraphicsScene::changed, this, &cConsole::sceneChanged);
0279 
0280   //background color
0281   setDefaultBkColor (Qt::black);
0282 
0283   //context menu
0284   setContextMenuPolicy (Qt::ActionsContextMenu);
0285   KActionCollection *acol = cActionManager::self()->getACol();
0286   QAction *showmenubar = acol->action ("ShowMenuBar");
0287   QAction *fullscreenmode = acol->action ("SetFullScreen");
0288   QAction *clipcopy = acol->action ("ClipboardCopy");
0289   QAction *pastemenu = acol->action ("PasteMenu");
0290   QAction *sep1 = new QAction (this);
0291   QAction *sep2 = new QAction (this);
0292   sep1->setSeparator (true);
0293   sep2->setSeparator (true);
0294   if (clipcopy) addAction (clipcopy);
0295   if (pastemenu) addAction (pastemenu);
0296   addAction (sep1);
0297   if (showmenubar) addAction (showmenubar);
0298   addAction (sep2);
0299   if (fullscreenmode) addAction (fullscreenmode);
0300 
0301   setFont (QFontDatabase::systemFont (QFontDatabase::FixedFont)); //default system fixed font
0302   viewport()->setCursor (Qt::IBeamCursor);
0303 
0304   forceBeginOfLine ();
0305   fixupOutput(true);
0306 }
0307 
0308 cConsole::~cConsole() {
0309   setScene (nullptr);  // needed to prevent crashes in the destructor
0310 
0311   delete d->scrollText;
0312   delete d->mainText;
0313   delete d->text;
0314   delete d;
0315 }
0316 
0317 void cConsole::setSession (int s) {
0318   d->sess = s;
0319 }
0320 
0321 void cConsole::setFont (QFont f) {
0322   d->font = f;
0323   d->text->setDefaultFont (d->font);
0324 
0325   QFontMetrics fm (f);
0326   d->charWidth = fm.horizontalAdvance ("m");
0327   d->charHeight = fm.lineSpacing() + 2;
0328 
0329   fixupOutput(true);
0330 }
0331 
0332 QFont cConsole::font () {
0333   return d->font;
0334 }
0335 
0336 void cConsole::setDefaultBkColor (QColor color) {
0337 
0338   d->bgcolor = color;
0339   QPalette pal = palette();
0340   pal.setColor (backgroundRole(), d->bgcolor);
0341   pal.setColor (QPalette::Base, d->bgcolor);
0342   setPalette (pal);
0343   setBackgroundRole (QPalette::Base);
0344   d->mainText->setBackgroundColor (color);
0345   d->scrollText->setBackgroundColor (color);
0346   update();
0347 }
0348 
0349 QColor cConsole::defaultBkColor () {
0350   return d->bgcolor;
0351 }
0352 
0353 void cConsole::setScrollTextVisible (bool vis)
0354 {
0355   d->scrollTextGroup->setVisible (vis);
0356 }
0357 
0358 void cConsole::sliderChanged (int val)
0359 {
0360   int maxval = verticalScrollBar()->maximum ();
0361 
0362   // if we're very close to the bottom, push the slider to the bottom
0363   // this fixes an off-by-one issue where scrollback would open when switching between tabs due to rounding issues
0364   if ((val < maxval) && (val >= maxval - 2)) {
0365     verticalScrollBar()->setValue (maxval);
0366     return;
0367   }
0368 
0369   d->atBottom = (val >= maxval);
0370   bool vis = (val < maxval);
0371   setScrollTextVisible (vis);
0372 }
0373 
0374 void cConsole::sceneChanged (const QList<QRectF> &)
0375 {
0376   // move back to the bottom if we were
0377   if (!d->atBottom) return;
0378   QScrollBar *sb  = verticalScrollBar();
0379   sb->setValue (sb->maximum());
0380 }
0381 
0382 void cConsole::setScrollTextSize (int aconsize)
0383 {
0384   d->scrollTextGroup->setPercentHeight (aconsize);
0385   adjustScrollBack();
0386 }
0387 
0388 void cConsole::setIndentation (int val) {
0389 
0390   d->indentChars = val;
0391 }
0392 
0393 void cConsole::setEnableBlinking (bool /*value*/) {
0394   // TODO
0395 }  
0396 
0397 int cConsole::curRows() {
0398   return height() / d->charHeight;
0399 }
0400 
0401 int cConsole::curCols() {
0402   if (d->charWidth <= 0) return 0;
0403   return width() / d->charWidth;
0404 }
0405 
0406 void cConsole::forceEmitSize () {
0407   emit dimensionsChanged (curCols(), curRows());
0408 }
0409 
0410 void cConsole::dumpBuffer (bool /*fromcurrent*/, QFile &file, char dumpType) {
0411   QString contents;
0412   // TODO: support 'fromcurrent'
0413   if ((dumpType == TRANSCRIPT_PLAIN) || (dumpType == TRANSCRIPT_ANSI))
0414     contents = d->text->toPlainText();
0415   else if (dumpType == TRANSCRIPT_HTML)
0416     contents = d->text->toHtml();
0417   file.write (contents.toLocal8Bit());
0418 }
0419 
0420 void cConsole::setHistorySize (int size) {
0421   d->historySize = size;
0422 }
0423 
0424 // grab all the words from the last 100 lines that meet the prefix/length criteria
0425 QStringList cConsole::words (QString prefix, int minLength) {
0426   QStringList res;
0427 
0428   int lineLimit = 100;
0429   int lines = 0;
0430   QTextBlock block = d->text->lastBlock();
0431   while ((block != d->text->firstBlock()) && (lines < lineLimit)) {
0432     lines += block.lineCount();
0433     QString text = block.text();
0434     // split the text into words
0435     QStringList words = text.split (QRegExp ("[\\s\\.\\,\\(\\)\\[\\]\\?\\!\\:\\;\"\']"));
0436     //store words that meet the requirements
0437     for (QString word : words) {
0438       if (word.length() < minLength) continue;
0439       if ((!prefix.isEmpty()) && (!word.startsWith (prefix, Qt::CaseInsensitive))) continue;
0440       // case-sensitive comparison here, so we store all the used upper/lowercase variants 
0441       if (res.contains (word)) continue;
0442 
0443       res.push_back (word);
0444     }
0445 
0446     block = block.previous();
0447   }
0448 
0449   // We intentionally don't sort the list.
0450   return res;
0451 }
0452 
0453 void cConsole::clear () {
0454   d->text->clear();
0455   update();
0456 }
0457 
0458 void cConsole::addLine (cTextChunk *chunk) {
0459   addNewText (chunk, true);
0460 }
0461 
0462 void cConsole::addText (cTextChunk *chunk) {
0463   addNewText (chunk, false);
0464 }
0465 
0466 int cConsole::totalLines() {
0467   return d->text->lineCount();
0468 }
0469 
0470 void cConsole::addNewText (cTextChunk *chunk, bool endTheLine)
0471 {
0472   QTextCursor cursor (d->text);
0473 
0474   cursor.beginEditBlock();
0475   cursor.movePosition (QTextCursor::End);
0476   if (chunk) {
0477     if (d->wantNewLine) {
0478       cursor.insertBlock ();
0479       cursor.block().layout()->setCacheEnabled (true);
0480       cConsoleTimestamp *stamp = new cConsoleTimestamp(chunk->getTimeStamp());
0481       cursor.block().setUserData (stamp);  // this will delete the stamp
0482       d->wantNewLine = false;
0483       QTextBlockFormat bformat = cursor.blockFormat();
0484       bformat.setForeground (Qt::lightGray);
0485       bformat.setProperty (QTextFormat::FramePadding, 1);
0486       bformat.setLineHeight (2, QTextBlockFormat::LineDistanceHeight);
0487       double px = d->indentChars * d->charWidth;  // 0 if no indentation is to happen
0488       bformat.setLeftMargin (px);
0489       bformat.setTextIndent (-1 * px);
0490       cursor.setBlockFormat (bformat);
0491     }
0492     chunk->insertToDocument (cursor);
0493   }
0494   cursor.endEditBlock();
0495   if (endTheLine) d->wantNewLine = true;
0496 
0497   while (totalLines() > d->historySize) {
0498     // check the height of the first block; if removing it won't put us below the history limit, remove it
0499     QTextBlock fblock = d->text->firstBlock();
0500     int flines = fblock.lineCount();
0501     if (totalLines() - flines < d->historySize) break;
0502 
0503     // determine the distance between the blocks ... can't just use the height of the bounding block, as that doesn't take spacing into account
0504     QTextBlock fnextblock = fblock.next();
0505     QRectF rect1 = d->text->documentLayout()->blockBoundingRect (fblock);
0506     QRectF rect2 = d->text->documentLayout()->blockBoundingRect (fnextblock);
0507     int fheight = rect2.top() - rect1.top();
0508 
0509     cursor = QTextCursor (fblock);
0510     cursor.select (QTextCursor::BlockUnderCursor);
0511     cursor.movePosition (QTextCursor::NextCharacter, QTextCursor::KeepAnchor);  // need to move, otherwise the empty block is kept
0512     cursor.removeSelectedText();
0513 
0514     // scroll accordingly if needed
0515     QScrollBar *bar = verticalScrollBar();
0516     if (!d->atBottom) bar->setValue (bar->value() - fheight);
0517   }
0518 
0519   fixupOutput(false);
0520 }
0521 
0522 void cConsole::forceBeginOfLine () {
0523   addNewText (nullptr, true);
0524 }
0525 
0526 void cConsole::expireNamedLinks (const QString & /*name*/) {
0527   // TODO
0528 }
0529 
0530 void cConsole::addSelectionToClipboard (QClipboard::Mode clipboardMode) {
0531   QString selection = d->mainText->textCursor().selectedText();
0532   if (!selection.size()) return;
0533   selection = selection.replace (QChar::ParagraphSeparator, '\n');
0534   QApplication::clipboard()->setText (selection, clipboardMode);
0535 }
0536 
0537 void cConsole::lineUp () {
0538   QScrollBar *bar = verticalScrollBar();
0539   int by = bar->singleStep();
0540   bar->setValue (bar->value() - by);
0541 }
0542 
0543 void cConsole::lineDown () {
0544   QScrollBar *bar = verticalScrollBar();
0545   int by = bar->singleStep();
0546   bar->setValue (bar->value() + by);
0547 }
0548 
0549 void cConsole::pageUp () {
0550   QScrollBar *bar = verticalScrollBar();
0551   int by = bar->pageStep() * (100 - d->scrollTextGroup->percentHeight()) / 100;  // deduct backview size
0552   bar->setValue (bar->value() - by);
0553 }
0554 
0555 void cConsole::pageDown () {
0556   QScrollBar *bar = verticalScrollBar();
0557   int by = bar->pageStep() * (100 - d->scrollTextGroup->percentHeight()) / 100;  // deduct backview size
0558   bar->setValue (bar->value() + by);
0559 }
0560 
0561 void cConsole::resizeEvent (QResizeEvent *)
0562 {
0563   fixupOutput(true);
0564 }
0565 
0566 // this is needed to resize the text display at startup
0567 bool cConsole::viewportEvent(QEvent *event)
0568 {
0569   if (event->type() == QEvent::Resize)
0570     fixupOutput(true);
0571   return QGraphicsView::viewportEvent (event);
0572 }
0573 
0574 
0575 void cConsole::adjustScrollBack ()
0576 {
0577   // move the scrollback to its desired position
0578   int h = d->scrollTextGroup->boundingRect().height();
0579   QPointF scenepos = mapToScene (0, height() - h);
0580   // don't exceed the scene height
0581   double diff = scenepos.y() + h - d->scene.height();
0582   if (diff > 0) scenepos.setY (scenepos.y() - diff);
0583   // and shift the text viewer
0584   double ty = d->scrollText->boundingRect().height() - h;
0585 
0586   d->scrollTextGroup->setPos (scenepos);
0587   d->scrollText->setPos (0, -ty);
0588 }
0589 
0590 void cConsole::scrollContentsBy (int dx, int dy)
0591 {
0592   QGraphicsView::scrollContentsBy (dx, dy);
0593 
0594   adjustScrollBack();
0595 }
0596 
0597 void cConsole::fixupOutput (bool sizeChanged)
0598 {
0599   double h = max ((qreal) viewport()->height(), d->text->documentLayout()->documentSize().height());
0600   scene()->setSceneRect (0, 0, viewport()->width(), h);
0601 
0602   sceneChanged();
0603   d->mainText->updateSize();
0604   d->scrollText->updateSize();
0605   adjustScrollBack ();
0606 
0607   if (sizeChanged)
0608     forceEmitSize ();
0609 }
0610 
0611 void cConsole::linkHovered (const QString &link)
0612 {
0613   viewport()->setCursor (link.isEmpty() ? Qt::IBeamCursor : Qt::PointingHandCursor);
0614 }
0615 
0616 void cConsole::linkActivated (const QString &link)
0617 {
0618   // TODO - menus and such, need to encode them in the level text. For now we just use plain text.
0619   // see old code below for menu handling - except that we don't have the chunk pointer
0620 
0621   // get the first two words, they are iscommand/toprompt
0622   int pos = link.indexOf(' ');
0623   if (pos < 0) return;
0624   QString w1 = link.left(pos);
0625   QString ll = link.mid(pos + 1);
0626   pos = ll.indexOf(' ');
0627   QString w2 = ll.left(pos);
0628   QString cmd = ll.mid(pos + 1);
0629 
0630   if (w1 == "link") {
0631     // URL link
0632     QDesktopServices::openUrl (QUrl (cmd));
0633     return;
0634   }
0635 
0636   if (w2 == "prompt")
0637     emit promptCommand (cmd);
0638   else
0639     emit sendCommand (cmd);
0640 }
0641 
0642 
0643 
0644 /*
0645 void cConsole::activateLink (chunkLink *link, const QPoint &point)
0646 {
0647   //two modes of operation, depending on whether this is a command-link or a URL-link
0648   if (link->isCommand())
0649   {
0650     QString cmd = link->target();
0651     bool toprompt = link->toPrompt();
0652     bool ismenu = link->isMenu();
0653     if (ismenu)
0654     {
0655       //get rid of old menu, if any
0656       delete linkMenu;
0657       
0658       link->parseMenu();
0659       
0660       //create the menu
0661       menuChunk = link;
0662       linkMenu = new KMenu (this);
0663       
0664       //insert all the items
0665       list<menuItem>::const_iterator it;
0666       for (it = menuChunk->menu().begin(); it != menuChunk->menu().end(); ++it)
0667         linkMenu->addAction ((*it).caption);
0668       connect (linkMenu, SIGNAL (triggered (QAction *)), this, SLOT (linkMenuItemHandler (QAction *)));
0669       
0670       linkMenu->popup (point);
0671     }
0672     else
0673     {
0674       if (toprompt)
0675         emit promptCommand (cmd);
0676       else
0677         emit sendCommand (cmd);
0678     }
0679   }
0680   else
0681   {
0682     QString url = link->target();
0683     KToolInvocation::invokeBrowser (url);
0684   }
0685 }
0686 
0687 void cConsole::linkMenuItemHandler (QAction *item)
0688 {
0689   if (!linkMenu) return;
0690   int idx = linkMenu->actions().indexOf (item);
0691   if (idx == -1) return;  // not found
0692   bool toprompt = menuChunk->toPrompt();
0693   list<menuItem>::const_iterator it;
0694   QString cmd;
0695   it = menuChunk->menu().begin();
0696   for (int i = 0; i < idx; i++)
0697     ++it;
0698   cmd = (*it).command;
0699   if (toprompt)
0700     emit promptCommand (cmd);
0701   else
0702     emit sendCommand (cmd);
0703 
0704   linkMenu = 0;
0705   menuChunk = 0;
0706 }
0707 */
0708 
0709 #include "moc_cconsole.cpp"