X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Ffrontends%2Fqt%2FGuiView.cpp;h=82a6f173820eb82be645136b590f418c297ea5db;hb=70a71a82;hp=8f6bf071154f4b3ff12f0cc0b9b287df52f47efa;hpb=61d68d05bd0f2172ab9406f23dc2640e6b9ac56b;p=features.git diff --git a/src/frontends/qt/GuiView.cpp b/src/frontends/qt/GuiView.cpp index 8f6bf07115..82a6f17382 100644 --- a/src/frontends/qt/GuiView.cpp +++ b/src/frontends/qt/GuiView.cpp @@ -69,6 +69,8 @@ #include "Toolbars.h" #include "version.h" +#include "graphics/PreviewLoader.h" + #include "support/convert.h" #include "support/debug.h" #include "support/ExceptionMessage.h" @@ -523,6 +525,7 @@ public: /// QTimer statusbar_timer_; + QTimer statusbar_stats_timer_; /// auto-saving of buffers Timeout autosave_timeout_; @@ -546,6 +549,20 @@ public: /// flag against a race condition due to multiclicks, see bug #1119 bool in_show_; + + // Timers for statistic updates in buffer + /// Current time left to the nearest info update + int time_to_update = 1000; + ///Basic step for timer in ms. Basically reaction time for short selections + int const timer_rate = 500; + /// Real stats updates infrequently. First they take long time for big buffers, second + /// they are visible for fast-repeat keyboards even for mid documents. + int const default_stats_rate = 5000; + /// Detection of new selection, so we can react fast + bool already_in_selection_ = false; + /// Maximum size of "short" selection for which we can update with faster timer_rate + int const max_sel_chars = 5000; + }; QSet GuiView::GuiViewPrivate::busyBuffers; @@ -553,8 +570,9 @@ QSet GuiView::GuiViewPrivate::busyBuffers; GuiView::GuiView(int id) : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0), - command_execute_(false), minibuffer_focus_(false), toolbarsMovable_(true), - devel_mode_(false) + command_execute_(false), minibuffer_focus_(false), word_count_enabled_(true), + char_count_enabled_(true), char_nb_count_enabled_(false), + toolbarsMovable_(true), devel_mode_(false) { connect(this, SIGNAL(bufferViewChanged()), this, SLOT(onBufferViewChanged())); @@ -582,6 +600,9 @@ GuiView::GuiView(int id) } connect(&d.statusbar_timer_, SIGNAL(timeout()), this, SLOT(clearMessage())); + connect(&d.statusbar_stats_timer_, SIGNAL(timeout()), + this, SLOT(showStats())); + d.statusbar_stats_timer_.start(d.timer_rate); // We don't want to keep the window in memory if it is closed. setAttribute(Qt::WA_DeleteOnClose, true); @@ -612,6 +633,10 @@ GuiView::GuiView(int id) // For Drag&Drop. setAcceptDrops(true); + QFontMetrics const fm(statusBar()->fontMetrics()); + int const iconheight = max(int(d.normalIconSize), fm.height()); + QSize const iconsize(iconheight, iconheight); + // add busy indicator to statusbar search_mode mode = theGuiApp()->imageSearchMode(); QString fn = toqstr(lyx::libFileSearch("images", "busy", "svgz", mode).absFileName()); @@ -620,14 +645,34 @@ GuiView::GuiView(int id) // make busy indicator square with 5px margins busySVG->setMaximumSize(busySVG->height() - 5, busySVG->height() - 5); busySVG->hide(); + // Add cancel button + QPixmap ps = QIcon(getPixmap("images/", "process-stop", "svgz")).pixmap(iconsize); + GuiClickableLabel * processStop = new GuiClickableLabel(statusBar()); + processStop->setPixmap(ps); + processStop->setToolTip(qt_("Click here to stop export/output process")); + processStop->hide(); + statusBar()->addPermanentWidget(processStop); connect(&d.processing_thread_watcher_, SIGNAL(started()), busySVG, SLOT(show())); connect(&d.processing_thread_watcher_, SIGNAL(finished()), busySVG, SLOT(hide())); - connect(busySVG, SIGNAL(pressed()), this, SLOT(checkCancelBackground())); + connect(&d.processing_thread_watcher_, SIGNAL(started()), + processStop, SLOT(show())); + connect(&d.processing_thread_watcher_, SIGNAL(finished()), + processStop, SLOT(hide())); + connect(processStop, SIGNAL(pressed()), this, SLOT(checkCancelBackground())); - QFontMetrics const fm(statusBar()->fontMetrics()); + connect(this, SIGNAL(scriptKilled()), busySVG, SLOT(hide())); + connect(this, SIGNAL(scriptKilled()), processStop, SLOT(hide())); + + stat_counts_ = new GuiClickableLabel(statusBar()); + stat_counts_->setAlignment(Qt::AlignCenter); + stat_counts_->setFrameStyle(QFrame::StyledPanel); + stat_counts_->hide(); + statusBar()->addPermanentWidget(stat_counts_); + + connect(stat_counts_, SIGNAL(clicked()), this, SLOT(statsPressed())); zoom_slider_ = new QSlider(Qt::Horizontal, statusBar()); // Small size slider for macOS to prevent the status bar from enlarging @@ -678,7 +723,8 @@ GuiView::GuiView(int id) // QPalette palette = statusBar()->palette(); - zoom_value_ = new QLabel(statusBar()); + zoom_value_ = new GuiClickableLabel(statusBar()); + connect(zoom_value_, SIGNAL(pressed()), this, SLOT(showZoomContextMenu())); // zoom_value_->setPalette(palette); zoom_value_->setForegroundRole(statusBar()->foregroundRole()); zoom_value_->setFixedHeight(fm.height()); @@ -694,14 +740,11 @@ GuiView::GuiView(int id) statusBar()->setContextMenuPolicy(Qt::CustomContextMenu); connect(statusBar(), SIGNAL(customContextMenuRequested(QPoint)), - this, SLOT(showZoomContextMenu())); + this, SLOT(showStatusBarContextMenu())); // enable pinch to zoom grabGesture(Qt::PinchGesture); - int const iconheight = max(int(d.normalIconSize), fm.height()); - QSize const iconsize(iconheight, iconheight); - QPixmap shellescape = QIcon(getPixmap("images/", "emblem-shellescape", "svgz,png")).pixmap(iconsize); shell_escape_ = new QLabel(statusBar()); shell_escape_->setPixmap(shellescape); @@ -791,10 +834,19 @@ void GuiView::checkCancelBackground() int const ret = Alert::prompt(ttl, msg, 1, 1, _("&Cancel export"), _("Co&ntinue")); - if (ret == 0) + if (ret == 0) { Systemcall::killscript(); + // stop busy signal immediately so that in the subsequent + // "Export canceled" prompt the status bar icons are accurate. + Q_EMIT scriptKilled(); + } } +void GuiView::statsPressed() +{ + DispatchResult dr; + dispatch(FuncRequest(LFUN_STATISTICS), dr); +} void GuiView::zoomSliderMoved(int value) { @@ -829,6 +881,15 @@ void GuiView::zoomOutPressed() void GuiView::showZoomContextMenu() +{ + QMenu * menu = guiApp->menus().menu(toqstr("context-zoom"), * this); + if (!menu) + return; + menu->exec(QCursor::pos()); +} + + +void GuiView::showStatusBarContextMenu() { QMenu * menu = guiApp->menus().menu(toqstr("context-statusbar"), * this); if (!menu) @@ -952,6 +1013,9 @@ void GuiView::saveLayout() const settings.setValue("icon_size", toqstr(d.iconSize(iconSize()))); settings.setValue("zoom_value_visible", zoom_value_->isVisible()); settings.setValue("zoom_slider_visible", zoom_slider_->isVisible()); + settings.setValue("word_count_enabled", word_count_enabled_); + settings.setValue("char_count_enabled", char_count_enabled_); + settings.setValue("char_nb_count_enabled", char_nb_count_enabled_); } @@ -1001,6 +1065,11 @@ bool GuiView::restoreLayout() zoom_in_->setVisible(show_zoom_slider); zoom_out_->setVisible(show_zoom_slider); + word_count_enabled_ = settings.value("word_count_enabled", true).toBool(); + char_count_enabled_ = settings.value("char_count_enabled", true).toBool(); + char_nb_count_enabled_ = settings.value("char_nb_count_enabled", true).toBool(); + stat_counts_->setVisible(word_count_enabled_ || char_count_enabled_ || char_nb_count_enabled_); + if (guiApp->platformName() == "qt4x11" || guiApp->platformName() == "xcb") { QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint(); QSize size = settings.value("size", QSize(690, 510)).toSize(); @@ -1271,6 +1340,7 @@ void GuiView::closeEvent(QCloseEvent * close_event) // Make sure the timer time out will not trigger a statusbar update. d.statusbar_timer_.stop(); + d.statusbar_stats_timer_.stop(); // Saving fullscreen requires additional tweaks in the toolbar code. // It wouldn't also work under linux natively. @@ -1376,6 +1446,73 @@ void GuiView::clearMessage() d.statusbar_timer_.stop(); } +void GuiView::showStats() +{ + if (!statsEnabled()) + return; + + d.time_to_update -= d.timer_rate; + + BufferView * bv = currentBufferView(); + Buffer * buf = bv ? &bv->buffer() : nullptr; + if (!buf) { + stat_counts_->hide(); + return; + } + + Cursor const & cur = bv->cursor(); + + // we start new selection and need faster update + if (!d.already_in_selection_ && cur.selection()) + d.time_to_update = 0; + + if (d.time_to_update > 0) + return; + + DocIterator from, to; + if (cur.selection()) { + from = cur.selectionBegin(); + to = cur.selectionEnd(); + d.already_in_selection_ = true; + } else { + from = doc_iterator_begin(buf); + to = doc_iterator_end(buf); + d.already_in_selection_ = false; + } + + buf->updateStatistics(from, to); + + QStringList stats; + if (word_count_enabled_) { + int const words = buf->wordCount(); + if (words == 1) + stats << toqstr(bformat(_("%1$d Word"), words)); + else + stats << toqstr(bformat(_("%1$d Words"), words)); + } + int const chars_with_blanks = buf->charCount(true); + if (char_count_enabled_) { + if (chars_with_blanks == 1) + stats << toqstr(bformat(_("%1$d Character"), chars_with_blanks)); + else + stats << toqstr(bformat(_("%1$d Characters"), chars_with_blanks)); + } + if (char_nb_count_enabled_) { + int const chars = buf->charCount(false); + if (chars == 1) + stats << toqstr(bformat(_("%1$d Character (no Blanks)"), chars)); + else + stats << toqstr(bformat(_("%1$d Characters (no Blanks)"), chars)); + } + stat_counts_->setText(stats.join(qt_(", [[stats separator]]"))); + stat_counts_->show(); + + d.time_to_update = d.default_stats_rate; + // fast updates for small selections + if (chars_with_blanks < d.max_sel_chars && cur.selection()) + d.time_to_update = d.timer_rate; +} + void GuiView::updateWindowTitle(GuiWorkArea * wa) { @@ -1515,6 +1652,12 @@ void GuiView::showMessage() } +bool GuiView::statsEnabled() const +{ + return word_count_enabled_ || char_count_enabled_ || char_nb_count_enabled_; +} + + bool GuiView::event(QEvent * e) { switch (e->type()) @@ -2419,6 +2562,12 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag) flag.setOnOff(zoom_value_ ? zoom_value_->isVisible() : false); } else if (cmd.argument() == "zoomslider") { flag.setOnOff(zoom_slider_ ? zoom_slider_->isVisible() : false); + } else if (cmd.argument() == "statistics-w") { + flag.setOnOff(word_count_enabled_); + } else if (cmd.argument() == "statistics-cb") { + flag.setOnOff(char_count_enabled_); + } else if (cmd.argument() == "statistics-c") { + flag.setOnOff(char_nb_count_enabled_); } else flag.setOnOff(isFullScreen()); break; @@ -2594,7 +2743,8 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag) case LFUN_WINDOW_RAISE: break; case LFUN_FORWARD_SEARCH: - enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty()); + enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty()) && + doc_buffer && doc_buffer->isSyncTeXenabled(); break; case LFUN_FILE_INSERT_PLAINTEXT: @@ -4759,6 +4909,10 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr) lyxrc.currentZoom, lyxrc.defaultZoom)); guiApp->fontLoader().update(); + // Regenerate instant previews + if (lyxrc.preview != LyXRC::PREVIEW_OFF + && doc_buffer && doc_buffer->loader()) + doc_buffer->loader()->refreshPreviews(); dr.screenUpdate(Update::ForceAll | Update::FitCursor); break; } @@ -4817,10 +4971,12 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr) dr.setMessage(_("Please, preview the document first.")); break; } + bool const goto_dvi = have_dvi && !lyxrc.forward_search_dvi.empty(); + bool const goto_pdf = have_pdf && !lyxrc.forward_search_pdf.empty(); string outname = dviname.onlyFileName(); string command = lyxrc.forward_search_dvi; - if (!have_dvi || (have_pdf && - pdfname.lastModified() > dviname.lastModified())) { + if ((!goto_dvi || goto_pdf) && + pdfname.lastModified() > dviname.lastModified()) { outname = pdfname.onlyFileName(); command = lyxrc.forward_search_pdf; } @@ -4904,6 +5060,18 @@ bool GuiView::lfunUiToggle(string const & ui_component) zoom_slider_->setVisible(!zoom_slider_->isVisible()); zoom_in_->setVisible(zoom_slider_->isVisible()); zoom_out_->setVisible(zoom_slider_->isVisible()); + } else if (ui_component == "statistics-w") { + word_count_enabled_ = !word_count_enabled_; + if (statsEnabled()) + showStats(); + } else if (ui_component == "statistics-cb") { + char_count_enabled_ = !char_count_enabled_; + if (statsEnabled()) + showStats(); + } else if (ui_component == "statistics-c") { + char_nb_count_enabled_ = !char_nb_count_enabled_; + if (statsEnabled()) + showStats(); } else if (ui_component == "frame") { int const l = contentsMargins().left(); @@ -4925,6 +5093,7 @@ bool GuiView::lfunUiToggle(string const & ui_component) toggleFullScreen(); } else return false; + stat_counts_->setVisible(statsEnabled()); return true; }