#include "Toolbars.h"
#include "version.h"
+#include "graphics/PreviewLoader.h"
+
#include "support/convert.h"
#include "support/debug.h"
#include "support/ExceptionMessage.h"
///
QTimer statusbar_timer_;
+ QTimer statusbar_stats_timer_;
/// auto-saving of buffers
Timeout autosave_timeout_;
/// 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<Buffer const *> 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()));
}
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);
// 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());
// 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
// 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());
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);
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)
{
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)
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_);
}
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();
// 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.
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)
{
}
+bool GuiView::statsEnabled() const
+{
+ return word_count_enabled_ || char_count_enabled_ || char_nb_count_enabled_;
+}
+
+
bool GuiView::event(QEvent * e)
{
switch (e->type())
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;
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:
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;
}
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;
}
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();
toggleFullScreen();
} else
return false;
+ stat_counts_->setVisible(statsEnabled());
return true;
}