VirtualBox

source: vbox/trunk/src/VBox/Debugger/VBoxDbgConsole.cpp@ 38813

最後變更 在這個檔案從38813是 38813,由 vboxsync 提交於 14 年 前

VBoxDbgConsole: Fixed busted text handling wrt selections and cursor positioning.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 18.6 KB
 
1/* $Id: VBoxDbgConsole.cpp 38813 2011-09-21 12:28:27Z vboxsync $ */
2/** @file
3 * VBox Debugger GUI - Console.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_DBGG
23#include "VBoxDbgConsole.h"
24
25#include <QLabel>
26#include <QApplication>
27#include <QFont>
28#include <QLineEdit>
29#include <QHBoxLayout>
30#include <QAction>
31#include <QContextMenuEvent>
32
33#include <VBox/dbg.h>
34#include <VBox/vmm/cfgm.h>
35#include <VBox/err.h>
36
37#include <iprt/thread.h>
38#include <iprt/tcp.h>
39#include <VBox/log.h>
40#include <iprt/assert.h>
41#include <iprt/asm.h>
42#include <iprt/alloc.h>
43#include <iprt/string.h>
44
45
46
47
48/*
49 *
50 * V B o x D b g C o n s o l e O u t p u t
51 * V B o x D b g C o n s o l e O u t p u t
52 * V B o x D b g C o n s o l e O u t p u t
53 *
54 *
55 */
56
57
58VBoxDbgConsoleOutput::VBoxDbgConsoleOutput(QWidget *pParent/* = NULL*/, const char *pszName/* = NULL*/)
59 : QTextEdit(pParent), m_uCurLine(0), m_uCurPos(0), m_hGUIThread(RTThreadNativeSelf())
60{
61 setReadOnly(true);
62 setUndoRedoEnabled(false);
63 setOverwriteMode(false);
64 setPlainText("");
65 setTextInteractionFlags(Qt::TextBrowserInteraction);
66 setAutoFormatting(QTextEdit::AutoAll);
67 setTabChangesFocus(true);
68 setAcceptRichText(false);
69
70#ifdef Q_WS_MAC
71 QFont Font("Monaco", 10, QFont::Normal, FALSE);
72 Font.setStyleStrategy(QFont::NoAntialias);
73#else
74 QFont Font = font();
75 Font.setStyleHint(QFont::TypeWriter);
76 Font.setFamily("Courier [Monotype]");
77#endif
78 setFont(Font);
79
80 /* green on black */
81 QPalette Pal(palette());
82 Pal.setColor(QPalette::All, QPalette::Base, QColor(Qt::black));
83 setPalette(Pal);
84 setTextColor(QColor(qRgb(0, 0xe0, 0)));
85 NOREF(pszName);
86}
87
88
89VBoxDbgConsoleOutput::~VBoxDbgConsoleOutput()
90{
91 Assert(m_hGUIThread == RTThreadNativeSelf());
92}
93
94
95void
96VBoxDbgConsoleOutput::appendText(const QString &rStr, bool fClearSelection)
97{
98 Assert(m_hGUIThread == RTThreadNativeSelf());
99
100 if (rStr.isEmpty() || rStr.isNull() || !rStr.length())
101 return;
102
103 /*
104 * Insert all in one go and make sure it's visible.
105 *
106 * We need to move the cursor and unselect any selected text before
107 * inserting anything, otherwise, text will disappear.
108 */
109 QTextCursor Cursor = textCursor();
110 if (!fClearSelection && Cursor.hasSelection())
111 {
112 QTextCursor SavedCursor = Cursor;
113 Cursor.clearSelection();
114 Cursor.movePosition(QTextCursor::End);
115
116 Cursor.insertText(rStr);
117
118 setTextCursor(SavedCursor);
119 }
120 else
121 {
122 if (Cursor.hasSelection())
123 Cursor.clearSelection();
124 if (!Cursor.atEnd())
125 Cursor.movePosition(QTextCursor::End);
126
127 Cursor.insertText(rStr);
128
129 setTextCursor(Cursor);
130 ensureCursorVisible();
131 }
132}
133
134
135
136
137/*
138 *
139 * V B o x D b g C o n s o l e I n p u t
140 * V B o x D b g C o n s o l e I n p u t
141 * V B o x D b g C o n s o l e I n p u t
142 *
143 *
144 */
145
146
147VBoxDbgConsoleInput::VBoxDbgConsoleInput(QWidget *pParent/* = NULL*/, const char *pszName/* = NULL*/)
148 : QComboBox(pParent), m_iBlankItem(0), m_hGUIThread(RTThreadNativeSelf())
149{
150 insertItem(m_iBlankItem, "");
151 setEditable(true);
152 setInsertPolicy(NoInsert);
153 setAutoCompletion(false);
154 setMaxCount(50);
155 const QLineEdit *pEdit = lineEdit();
156 if (pEdit)
157 connect(pEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
158
159 NOREF(pszName);
160}
161
162
163VBoxDbgConsoleInput::~VBoxDbgConsoleInput()
164{
165 Assert(m_hGUIThread == RTThreadNativeSelf());
166}
167
168
169void
170VBoxDbgConsoleInput::setLineEdit(QLineEdit *pEdit)
171{
172 Assert(m_hGUIThread == RTThreadNativeSelf());
173 QComboBox::setLineEdit(pEdit);
174 if (lineEdit() == pEdit && pEdit)
175 connect(pEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
176}
177
178
179void
180VBoxDbgConsoleInput::returnPressed()
181{
182 Assert(m_hGUIThread == RTThreadNativeSelf());
183 /* deal with the current command. */
184 QString Str = currentText();
185 emit commandSubmitted(Str);
186
187 /* update the history and clear the entry field */
188 QString PrevStr = m_iBlankItem > 0 ? itemText(m_iBlankItem - 1) : "";
189 if (PrevStr != Str)
190 {
191 setItemText(m_iBlankItem, Str);
192 if ( m_iBlankItem > 0
193 && m_iBlankItem >= maxCount() - 1)
194 removeItem(m_iBlankItem - maxCount() - 1);
195 insertItem(++m_iBlankItem, "");
196 }
197
198 clearEditText();
199 setCurrentIndex(m_iBlankItem);
200}
201
202
203
204
205
206
207/*
208 *
209 * V B o x D b g C o n s o l e
210 * V B o x D b g C o n s o l e
211 * V B o x D b g C o n s o l e
212 *
213 *
214 */
215
216
217VBoxDbgConsole::VBoxDbgConsole(VBoxDbgGui *a_pDbgGui, QWidget *a_pParent/* = NULL*/)
218 : VBoxDbgBaseWindow(a_pDbgGui, a_pParent), m_pOutput(NULL), m_pInput(NULL), m_fInputRestoreFocus(false),
219 m_pszInputBuf(NULL), m_cbInputBuf(0), m_cbInputBufAlloc(0),
220 m_pszOutputBuf(NULL), m_cbOutputBuf(0), m_cbOutputBufAlloc(0),
221 m_pTimer(NULL), m_fUpdatePending(false), m_Thread(NIL_RTTHREAD), m_EventSem(NIL_RTSEMEVENT),
222 m_fTerminate(false), m_fThreadTerminated(false)
223{
224 setWindowTitle("VBoxDbg - Console");
225
226 /*
227 * Create the output text box.
228 */
229 m_pOutput = new VBoxDbgConsoleOutput(this);
230
231 /* try figure a suitable size */
232 QLabel *pLabel = new QLabel( "11111111111111111111111111111111111111111111111111111111111111111111111111111112222222222", this);
233 pLabel->setFont(m_pOutput->font());
234 QSize Size = pLabel->sizeHint();
235 delete pLabel;
236 Size.setWidth((int)(Size.width() * 1.10));
237 Size.setHeight(Size.width() / 2);
238 resize(Size);
239
240 /*
241 * Create the input combo box (with a label).
242 */
243 QHBoxLayout *pLayout = new QHBoxLayout();
244 //pLayout->setSizeConstraint(QLayout::SetMaximumSize);
245
246 pLabel = new QLabel(" Command ");
247 pLayout->addWidget(pLabel);
248 pLabel->setMaximumSize(pLabel->sizeHint());
249 pLabel->setAlignment(Qt::AlignCenter);
250
251 m_pInput = new VBoxDbgConsoleInput(NULL);
252 pLayout->addWidget(m_pInput);
253 m_pInput->setDuplicatesEnabled(false);
254 connect(m_pInput, SIGNAL(commandSubmitted(const QString &)), this, SLOT(commandSubmitted(const QString &)));
255
256# if 0//def Q_WS_MAC
257 pLabel = new QLabel(" ");
258 pLayout->addWidget(pLabel);
259 pLabel->setMaximumSize(20, m_pInput->sizeHint().height() + 6);
260 pLabel->setMinimumSize(20, m_pInput->sizeHint().height() + 6);
261# endif
262
263 QWidget *pHBox = new QWidget(this);
264 pHBox->setLayout(pLayout);
265
266 m_pInput->setEnabled(false); /* (we'll get a ready notification) */
267
268
269 /*
270 * Vertical layout box on the whole widget.
271 */
272 QVBoxLayout *pVLayout = new QVBoxLayout();
273 pVLayout->setContentsMargins(0, 0, 0, 0);
274 pVLayout->setSpacing(5);
275 pVLayout->addWidget(m_pOutput);
276 pVLayout->addWidget(pHBox);
277 setLayout(pVLayout);
278
279 /*
280 * The tab order is from input to output, not the other way around as it is by default.
281 */
282 setTabOrder(m_pInput, m_pOutput);
283
284 /*
285 * Setup the timer.
286 */
287 m_pTimer = new QTimer(this);
288 connect(m_pTimer, SIGNAL(timeout()), SLOT(updateOutput()));
289
290 /*
291 * Init the backend structure.
292 */
293 m_Back.Core.pfnInput = backInput;
294 m_Back.Core.pfnRead = backRead;
295 m_Back.Core.pfnWrite = backWrite;
296 m_Back.Core.pfnSetReady = backSetReady;
297 m_Back.pSelf = this;
298
299 /*
300 * Create the critical section, the event semaphore and the debug console thread.
301 */
302 int rc = RTCritSectInit(&m_Lock);
303 AssertRC(rc);
304
305 rc = RTSemEventCreate(&m_EventSem);
306 AssertRC(rc);
307
308 rc = RTThreadCreate(&m_Thread, backThread, this, 0, RTTHREADTYPE_DEBUGGER, RTTHREADFLAGS_WAITABLE, "VBoxDbgC");
309 AssertRC(rc);
310 if (RT_FAILURE(rc))
311 m_Thread = NIL_RTTHREAD;
312
313 /*
314 * Shortcuts.
315 */
316 m_pFocusToInput = new QAction("", this);
317 m_pFocusToInput->setShortcut(QKeySequence("Ctrl+L"));
318 addAction(m_pFocusToInput);
319 connect(m_pFocusToInput, SIGNAL(triggered(bool)), this, SLOT(actFocusToInput()));
320
321 m_pFocusToOutput = new QAction("", this);
322 m_pFocusToOutput->setShortcut(QKeySequence("Ctrl+O"));
323 addAction(m_pFocusToOutput);
324 connect(m_pFocusToOutput, SIGNAL(triggered(bool)), this, SLOT(actFocusToOutput()));
325}
326
327
328VBoxDbgConsole::~VBoxDbgConsole()
329{
330 Assert(isGUIThread());
331
332 /*
333 * Wait for the thread.
334 */
335 ASMAtomicWriteBool(&m_fTerminate, true);
336 RTSemEventSignal(m_EventSem);
337 if (m_Thread != NIL_RTTHREAD)
338 {
339 int rc = RTThreadWait(m_Thread, 15000, NULL);
340 AssertRC(rc);
341 m_Thread = NIL_RTTHREAD;
342 }
343
344 /*
345 * Free resources.
346 */
347 delete m_pTimer;
348 m_pTimer = NULL;
349 RTCritSectDelete(&m_Lock);
350 RTSemEventDestroy(m_EventSem);
351 m_EventSem = 0;
352 m_pOutput = NULL;
353 m_pInput = NULL;
354 if (m_pszInputBuf)
355 {
356 RTMemFree(m_pszInputBuf);
357 m_pszInputBuf = NULL;
358 }
359 m_cbInputBuf = 0;
360 m_cbInputBufAlloc = 0;
361
362 delete m_pFocusToInput;
363 m_pFocusToInput = NULL;
364 delete m_pFocusToOutput;
365 m_pFocusToOutput = NULL;
366}
367
368
369void
370VBoxDbgConsole::commandSubmitted(const QString &rCommand)
371{
372 Assert(isGUIThread());
373
374 lock();
375 RTSemEventSignal(m_EventSem);
376
377 QByteArray Utf8Array = rCommand.toUtf8();
378 const char *psz = Utf8Array.constData();
379 size_t cb = strlen(psz);
380
381 /*
382 * Make sure we've got space for the input.
383 */
384 if (cb + m_cbInputBuf >= m_cbInputBufAlloc)
385 {
386 size_t cbNew = RT_ALIGN_Z(cb + m_cbInputBufAlloc + 1, 128);
387 void *pv = RTMemRealloc(m_pszInputBuf, cbNew);
388 if (!pv)
389 {
390 unlock();
391 return;
392 }
393 m_pszInputBuf = (char *)pv;
394 m_cbInputBufAlloc = cbNew;
395 }
396
397 /*
398 * Add the input and output it.
399 */
400 memcpy(m_pszInputBuf + m_cbInputBuf, psz, cb);
401 m_cbInputBuf += cb;
402 m_pszInputBuf[m_cbInputBuf++] = '\n';
403
404 m_pOutput->appendText(rCommand + "\n", true /*fClearSelection*/);
405 m_pOutput->ensureCursorVisible();
406
407 m_fInputRestoreFocus = m_pInput->hasFocus(); /* dirty focus hack */
408 m_pInput->setEnabled(false);
409
410 Log(("VBoxDbgConsole::commandSubmitted: %s (input-enabled=%RTbool)\n", psz, m_pInput->isEnabled()));
411 unlock();
412}
413
414
415void
416VBoxDbgConsole::updateOutput()
417{
418 Assert(isGUIThread());
419
420 lock();
421 m_fUpdatePending = false;
422 if (m_cbOutputBuf)
423 {
424 m_pOutput->appendText(QString::fromUtf8((const char *)m_pszOutputBuf, (int)m_cbOutputBuf), false /*fClearSelection*/);
425 m_cbOutputBuf = 0;
426 }
427 unlock();
428}
429
430
431/**
432 * Lock the object.
433 */
434void
435VBoxDbgConsole::lock()
436{
437 RTCritSectEnter(&m_Lock);
438}
439
440
441/**
442 * Unlocks the object.
443 */
444void
445VBoxDbgConsole::unlock()
446{
447 RTCritSectLeave(&m_Lock);
448}
449
450
451
452/**
453 * Checks if there is input.
454 *
455 * @returns true if there is input ready.
456 * @returns false if there not input ready.
457 * @param pBack Pointer to VBoxDbgConsole::m_Back.
458 * @param cMillies Number of milliseconds to wait on input data.
459 */
460/*static*/ DECLCALLBACK(bool)
461VBoxDbgConsole::backInput(PDBGCBACK pBack, uint32_t cMillies)
462{
463 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
464 pThis->lock();
465
466 bool fRc = true;
467 if (!pThis->m_cbInputBuf)
468 {
469 /*
470 * Wait outside the lock for the requested time, then check again.
471 */
472 pThis->unlock();
473 RTSemEventWait(pThis->m_EventSem, cMillies);
474 pThis->lock();
475 fRc = pThis->m_cbInputBuf
476 || ASMAtomicUoReadBool(&pThis->m_fTerminate);
477 }
478
479 pThis->unlock();
480 return fRc;
481}
482
483
484/**
485 * Read input.
486 *
487 * @returns VBox status code.
488 * @param pBack Pointer to VBoxDbgConsole::m_Back.
489 * @param pvBuf Where to put the bytes we read.
490 * @param cbBuf Maximum nymber of bytes to read.
491 * @param pcbRead Where to store the number of bytes actually read.
492 * If NULL the entire buffer must be filled for a
493 * successful return.
494 */
495/*static*/ DECLCALLBACK(int)
496VBoxDbgConsole::backRead(PDBGCBACK pBack, void *pvBuf, size_t cbBuf, size_t *pcbRead)
497{
498 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
499 Assert(pcbRead); /** @todo implement this bit */
500 if (pcbRead)
501 *pcbRead = 0;
502
503 pThis->lock();
504 int rc = VINF_SUCCESS;
505 if (!ASMAtomicUoReadBool(&pThis->m_fTerminate))
506 {
507 if (pThis->m_cbInputBuf)
508 {
509 const char *psz = pThis->m_pszInputBuf;
510 size_t cbRead = RT_MIN(pThis->m_cbInputBuf, cbBuf);
511 memcpy(pvBuf, psz, cbRead);
512 psz += cbRead;
513 pThis->m_cbInputBuf -= cbRead;
514 if (*psz)
515 memmove(pThis->m_pszInputBuf, psz, pThis->m_cbInputBuf);
516 pThis->m_pszInputBuf[pThis->m_cbInputBuf] = '\0';
517 *pcbRead = cbRead;
518 }
519 }
520 else
521 rc = VERR_GENERAL_FAILURE;
522 pThis->unlock();
523 return rc;
524}
525
526
527/**
528 * Write (output).
529 *
530 * @returns VBox status code.
531 * @param pBack Pointer to VBoxDbgConsole::m_Back.
532 * @param pvBuf What to write.
533 * @param cbBuf Number of bytes to write.
534 * @param pcbWritten Where to store the number of bytes actually written.
535 * If NULL the entire buffer must be successfully written.
536 */
537/*static*/ DECLCALLBACK(int)
538VBoxDbgConsole::backWrite(PDBGCBACK pBack, const void *pvBuf, size_t cbBuf, size_t *pcbWritten)
539{
540 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
541 int rc = VINF_SUCCESS;
542
543 pThis->lock();
544 if (cbBuf + pThis->m_cbOutputBuf >= pThis->m_cbOutputBufAlloc)
545 {
546 size_t cbNew = RT_ALIGN_Z(cbBuf + pThis->m_cbOutputBufAlloc + 1, 1024);
547 void *pv = RTMemRealloc(pThis->m_pszOutputBuf, cbNew);
548 if (!pv)
549 {
550 pThis->unlock();
551 if (pcbWritten)
552 *pcbWritten = 0;
553 return VERR_NO_MEMORY;
554 }
555 pThis->m_pszOutputBuf = (char *)pv;
556 pThis->m_cbOutputBufAlloc = cbNew;
557 }
558
559 /*
560 * Add the output.
561 */
562 memcpy(pThis->m_pszOutputBuf + pThis->m_cbOutputBuf, pvBuf, cbBuf);
563 pThis->m_cbOutputBuf += cbBuf;
564 pThis->m_pszOutputBuf[pThis->m_cbOutputBuf] = '\0';
565 if (pcbWritten)
566 *pcbWritten = cbBuf;
567
568 if (ASMAtomicUoReadBool(&pThis->m_fTerminate))
569 rc = VERR_GENERAL_FAILURE;
570
571 /*
572 * Tell the GUI thread to draw this text.
573 * We cannot do it from here without frequent crashes.
574 */
575 if (!pThis->m_fUpdatePending)
576 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(VBoxDbgConsoleEvent::kUpdate));
577
578 pThis->unlock();
579
580 return rc;
581}
582
583
584/*static*/ DECLCALLBACK(void)
585VBoxDbgConsole::backSetReady(PDBGCBACK pBack, bool fReady)
586{
587 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
588 if (fReady)
589 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(VBoxDbgConsoleEvent::kInputEnable));
590}
591
592
593/**
594 * The Debugger Console Thread
595 *
596 * @returns VBox status code (ignored).
597 * @param Thread The thread handle.
598 * @param pvUser Pointer to the VBoxDbgConsole object.s
599 */
600/*static*/ DECLCALLBACK(int)
601VBoxDbgConsole::backThread(RTTHREAD Thread, void *pvUser)
602{
603 VBoxDbgConsole *pThis = (VBoxDbgConsole *)pvUser;
604 LogFlow(("backThread: Thread=%p pvUser=%p\n", (void *)Thread, pvUser));
605
606 NOREF(Thread);
607
608 /*
609 * Create and execute the console.
610 */
611 int rc = pThis->dbgcCreate(&pThis->m_Back.Core, 0);
612
613 ASMAtomicUoWriteBool(&pThis->m_fThreadTerminated, true);
614 if (!ASMAtomicUoReadBool(&pThis->m_fTerminate))
615 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(rc == VINF_SUCCESS
616 ? VBoxDbgConsoleEvent::kTerminatedUser
617 : VBoxDbgConsoleEvent::kTerminatedOther));
618 LogFlow(("backThread: returns %Rrc (m_fTerminate=%RTbool)\n", rc, ASMAtomicUoReadBool(&pThis->m_fTerminate)));
619 return rc;
620}
621
622
623bool
624VBoxDbgConsole::event(QEvent *pGenEvent)
625{
626 Assert(isGUIThread());
627 if (pGenEvent->type() == (QEvent::Type)VBoxDbgConsoleEvent::kEventNumber)
628 {
629 VBoxDbgConsoleEvent *pEvent = (VBoxDbgConsoleEvent *)pGenEvent;
630
631 switch (pEvent->command())
632 {
633 /* make update pending. */
634 case VBoxDbgConsoleEvent::kUpdate:
635 lock();
636 if (!m_fUpdatePending)
637 {
638 m_fUpdatePending = true;
639 m_pTimer->setSingleShot(true);
640 m_pTimer->start(10);
641 }
642 unlock();
643 break;
644
645 /* Re-enable the input field and restore focus. */
646 case VBoxDbgConsoleEvent::kInputEnable:
647 Log(("VBoxDbgConsole: kInputEnable (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
648 m_pInput->setEnabled(true);
649 if ( m_fInputRestoreFocus
650 && !m_pInput->hasFocus())
651 m_pInput->setFocus(); /* this is a hack. */
652 m_fInputRestoreFocus = false;
653 break;
654
655 /* The thread terminated by user command (exit, quit, bye). */
656 case VBoxDbgConsoleEvent::kTerminatedUser:
657 Log(("VBoxDbgConsole: kTerminatedUser (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
658 m_pInput->setEnabled(false);
659 close();
660 break;
661
662 /* The thread terminated for some unknown reason., disable input */
663 case VBoxDbgConsoleEvent::kTerminatedOther:
664 Log(("VBoxDbgConsole: kTerminatedOther (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
665 m_pInput->setEnabled(false);
666 break;
667
668 /* paranoia */
669 default:
670 AssertMsgFailed(("command=%d\n", pEvent->command()));
671 break;
672 }
673 return true;
674 }
675
676 return VBoxDbgBaseWindow::event(pGenEvent);
677}
678
679
680void
681VBoxDbgConsole::closeEvent(QCloseEvent *a_pCloseEvt)
682{
683 if (m_fThreadTerminated)
684 {
685 a_pCloseEvt->accept();
686 delete this;
687 }
688}
689
690
691void
692VBoxDbgConsole::actFocusToInput()
693{
694 if (!m_pInput->hasFocus())
695 m_pInput->setFocus(Qt::ShortcutFocusReason);
696}
697
698
699void
700VBoxDbgConsole::actFocusToOutput()
701{
702 if (!m_pOutput->hasFocus())
703 m_pOutput->setFocus(Qt::ShortcutFocusReason);
704}
705
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette