diff options
| -rw-r--r-- | cocoa/ScintillaCocoa.h | 7 | ||||
| -rw-r--r-- | cocoa/ScintillaCocoa.mm | 109 | ||||
| -rw-r--r-- | doc/ScintillaHistory.html | 8 | ||||
| -rw-r--r-- | gtk/ScintillaGTK.cxx | 57 | ||||
| -rw-r--r-- | qt/ScintillaEditBase/ScintillaQt.cpp | 66 | ||||
| -rw-r--r-- | qt/ScintillaEditBase/ScintillaQt.h | 10 | ||||
| -rw-r--r-- | src/Editor.cxx | 124 | ||||
| -rw-r--r-- | src/Editor.h | 8 | ||||
| -rw-r--r-- | win32/ScintillaWin.cxx | 65 | 
9 files changed, 359 insertions, 95 deletions
| diff --git a/cocoa/ScintillaCocoa.h b/cocoa/ScintillaCocoa.h index 61d78c449..122df7b44 100644 --- a/cocoa/ScintillaCocoa.h +++ b/cocoa/ScintillaCocoa.h @@ -144,7 +144,11 @@ public:    void PaintMargin(NSRect aRect);    virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam); -  void SetTicking(bool on); +  void TickFor(TickReason reason); +  bool FineTickerAvailable(); +  bool FineTickerRunning(TickReason reason); +  void FineTickerStart(TickReason reason, int millis, int tolerance); +  void FineTickerCancel(TickReason reason);    bool SetIdle(bool on);    void SetMouseCapture(bool on);    bool HaveMouseCapture(); @@ -181,6 +185,7 @@ public:    static sptr_t DirectFunction(sptr_t ptr, unsigned int iMessage, uptr_t wParam, sptr_t lParam); +  NSTimer *timers[tickPlatform+1];    void TimerFired(NSTimer* timer);    void IdleTimerFired();    static void UpdateObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *sci); diff --git a/cocoa/ScintillaCocoa.mm b/cocoa/ScintillaCocoa.mm index 027bdffbf..f8c53932c 100644 --- a/cocoa/ScintillaCocoa.mm +++ b/cocoa/ScintillaCocoa.mm @@ -395,6 +395,10 @@ ScintillaCocoa::ScintillaCocoa(SCIContentView* view, SCIMarginView* viewMargin)    idleTimer = NULL;    observer = NULL;    layerFindIndicator = NULL; +  for (TickReason tr=tickCaret; tr<=tickPlatform; tr = static_cast<TickReason>(tr+1)) +  { +    timers[tr] = nil; +  }    Initialise();  } @@ -435,7 +439,10 @@ void ScintillaCocoa::Initialise()  void ScintillaCocoa::Finalise()  {    ObserverRemove(); -  SetTicking(false); +  for (TickReason tr=tickCaret; tr<=tickPlatform; tr = static_cast<TickReason>(tr+1)) +  { +    FineTickerCancel(tr); +  }    ScintillaBase::Finalise();  } @@ -862,32 +869,72 @@ sptr_t ScintillaCocoa::DefWndProc(unsigned int, uptr_t, sptr_t)  //--------------------------------------------------------------------------------------------------  /** - * Enables or disables a timer that can trigger background processing at a regular interval, like - * drag scrolling or caret blinking. + * Handle any ScintillaCocoa-specific ticking or call superclass.   */ -void ScintillaCocoa::SetTicking(bool on) +void ScintillaCocoa::TickFor(TickReason reason)  { -  if (timer.ticking != on) +  if (reason == tickPlatform)    { -    timer.ticking = on; -    if (timer.ticking) -    { -      // Scintilla ticks = milliseconds -      tickTimer = [NSTimer scheduledTimerWithTimeInterval: timer.tickSize / 1000.0 -						   target: timerTarget -						 selector: @selector(timerFired:) -						 userInfo: nil -						  repeats: YES]; -      timer.tickerID = reinterpret_cast<TickerID>(tickTimer); -    } -    else -      if (timer.tickerID != NULL) -      { -        [reinterpret_cast<NSTimer*>(timer.tickerID) invalidate]; -        timer.tickerID = 0; -      } +    DragScroll(); +  } +  else +  { +    Editor::TickFor(reason); +  } +} + +//-------------------------------------------------------------------------------------------------- + +/** + * Report that this Editor subclass has a working implementation of FineTickerStart. + */ +bool ScintillaCocoa::FineTickerAvailable() +{ +  return true; +} + +//-------------------------------------------------------------------------------------------------- + +/** + * Is a particular timer currently running? + */ +bool ScintillaCocoa::FineTickerRunning(TickReason reason) +{ +  return timers[reason] != nil; +} + +//-------------------------------------------------------------------------------------------------- + +/** + * Start a fine-grained timer. + */ +void ScintillaCocoa::FineTickerStart(TickReason reason, int millis, int tolerance) +{ +  FineTickerCancel(reason); +  NSTimer *fineTimer = [NSTimer scheduledTimerWithTimeInterval: millis / 1000.0 +                                                        target: timerTarget +                                                      selector: @selector(timerFired:) +                                                      userInfo: nil +                                                       repeats: YES]; +  if (tolerance && [fineTimer respondsToSelector: @selector(setTolerance:)]) +  { +    [fineTimer setTolerance: tolerance / 1000.0]; +  } +  timers[reason] = fineTimer; +} + +//-------------------------------------------------------------------------------------------------- + +/** + * Cancel a fine-grained timer. + */ +void ScintillaCocoa::FineTickerCancel(TickReason reason) +{ +  if (timers[reason]) +  { +    [timers[reason] invalidate]; +    timers[reason] = nil;    } -  timer.ticksToWait = caret.period;  }  //-------------------------------------------------------------------------------------------------- @@ -1175,6 +1222,8 @@ void ScintillaCocoa::StartDrag()    inDragDrop = ddDragging; +  FineTickerStart(tickPlatform, timer.tickSize, 0); +    // Put the data to be dragged on the drag pasteboard.    SelectionText selectedText;    NSPasteboard* pasteboard = [NSPasteboard pasteboardWithName: NSDragPboard]; @@ -1336,6 +1385,7 @@ void ScintillaCocoa::StartDrag()   */  NSDragOperation ScintillaCocoa::DraggingEntered(id <NSDraggingInfo> info)  { +  FineTickerStart(tickPlatform, timer.tickSize, 0);    return DraggingUpdated(info);  } @@ -1378,6 +1428,7 @@ void ScintillaCocoa::DraggingExited(id <NSDraggingInfo> info)  {  #pragma unused(info)    SetDragPosition(SelectionPosition(invalidPosition)); +  FineTickerCancel(tickPlatform);    inDragDrop = ddNone;  } @@ -1822,9 +1873,13 @@ bool ScintillaCocoa::CanRedo()  void ScintillaCocoa::TimerFired(NSTimer* timer)  { -#pragma unused(timer) -  Tick(); -  DragScroll(); +  for (TickReason tr=tickCaret; tr<=tickPlatform; tr = static_cast<TickReason>(tr+1)) +  { +    if (timers[tr] == timer) +    { +      TickFor(tr); +    } +  }  }  //-------------------------------------------------------------------------------------------------- @@ -2145,7 +2200,7 @@ void ScintillaCocoa::ActiveStateChanged(bool isActive)    if (!isActive) {      DropCaret();      //SetFocusState( false ); -    SetTicking( false ); +    FineTickerCancel(tickCaret);    } else {      ShowCaretAtCurrentPosition();    } diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html index 08615b5cf..9dfbe08f0 100644 --- a/doc/ScintillaHistory.html +++ b/doc/ScintillaHistory.html @@ -469,6 +469,14 @@  	Released 3 July 2014.  	</li>  	<li> +	Separate timers are used for each type of periodic activity and they are turned on and off +	as required. This saves power as there are fewer wake ups. +	On recent releases of OS X Cocoa and Windows, coalescing timers are used to further +	save power. +	<a href="http://sourceforge.net/p/scintilla/bugs/1086/">Bug #1086</a>. +	<a href="http://sourceforge.net/p/scintilla/bugs/1532/">Bug #1532</a>. +	</li> +	<li>  	SciTE adds a "Clean" command to the "Tools" menu which is meant to be bound to a command like  	"make clean".  	</li> diff --git a/gtk/ScintillaGTK.cxx b/gtk/ScintillaGTK.cxx index 3285967da..be3df5501 100644 --- a/gtk/ScintillaGTK.cxx +++ b/gtk/ScintillaGTK.cxx @@ -189,7 +189,17 @@ public: 	// Public for scintilla_send_message  	virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);  private:  	virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam); -	virtual void SetTicking(bool on); +	struct TimeThunk { +		TickReason reason; +		ScintillaGTK *scintilla; +		guint timer; +		TimeThunk() : reason(tickCaret), scintilla(NULL), timer(0) {} +	}; +	TimeThunk timers[tickDwell+1]; +	virtual bool FineTickerAvailable(); +	virtual bool FineTickerRunning(TickReason reason); +	virtual void FineTickerStart(TickReason reason, int millis, int tolerance); +	virtual void FineTickerCancel(TickReason reason);  	virtual bool SetIdle(bool on);  	virtual void SetMouseCapture(bool on);  	virtual bool HaveMouseCapture(); @@ -303,7 +313,7 @@ private:  	                             gint x, gint y, GtkSelectionData *selection_data, guint info, guint time);  	static void DragDataGet(GtkWidget *widget, GdkDragContext *context,  	                        GtkSelectionData *selection_data, guint info, guint time); -	static gboolean TimeOut(ScintillaGTK *sciThis); +	static gboolean TimeOut(TimeThunk *tt);  	static gboolean IdleCallback(ScintillaGTK *sciThis);  	static gboolean StyleIdle(ScintillaGTK *sciThis);  	virtual void QueueIdleWork(WorkNeeded::workItems items, int upTo); @@ -832,11 +842,16 @@ void ScintillaGTK::Initialise() {  		caret.period = 0;  	} -	SetTicking(true); +	for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) { +		timers[tr].reason = tr; +		timers[tr].scintilla = this; +	}  }  void ScintillaGTK::Finalise() { -	SetTicking(false); +	for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) { +		FineTickerCancel(tr); +	}  	ScintillaBase::Finalise();  } @@ -1027,17 +1042,27 @@ sptr_t ScintillaGTK::DefWndProc(unsigned int, uptr_t, sptr_t) {  	return 0;  } -void ScintillaGTK::SetTicking(bool on) { -	if (timer.ticking != on) { -		timer.ticking = on; -		if (timer.ticking) { -			timer.tickerID = reinterpret_cast<TickerID>(g_timeout_add(timer.tickSize, -				reinterpret_cast<GSourceFunc>(TimeOut), this)); -		} else { -			g_source_remove(GPOINTER_TO_UINT(timer.tickerID)); -		} +/** +* Report that this Editor subclass has a working implementation of FineTickerStart. +*/ +bool ScintillaGTK::FineTickerAvailable() { +	return true; +} + +bool ScintillaGTK::FineTickerRunning(TickReason reason) { +	return timers[reason].timer != 0; +} + +void ScintillaGTK::FineTickerStart(TickReason reason, int millis, int /* tolerance */) { +	FineTickerCancel(reason); +	timers[reason].timer = g_timeout_add(millis, reinterpret_cast<GSourceFunc>(TimeOut), &timers[reason]); +} + +void ScintillaGTK::FineTickerCancel(TickReason reason) { +	if (timers[reason].timer) { +		g_source_remove(timers[reason].timer); +		timers[reason].timer = 0;  	} -	timer.ticksToWait = caret.period;  }  bool ScintillaGTK::SetIdle(bool on) { @@ -2715,8 +2740,8 @@ void ScintillaGTK::DragDataGet(GtkWidget *widget, GdkDragContext *context,  	}  } -int ScintillaGTK::TimeOut(ScintillaGTK *sciThis) { -	sciThis->Tick(); +int ScintillaGTK::TimeOut(TimeThunk *tt) { +	tt->scintilla->TickFor(tt->reason);  	return 1;  } diff --git a/qt/ScintillaEditBase/ScintillaQt.cpp b/qt/ScintillaEditBase/ScintillaQt.cpp index 3d727900b..c2b250a3e 100644 --- a/qt/ScintillaEditBase/ScintillaQt.cpp +++ b/qt/ScintillaEditBase/ScintillaQt.cpp @@ -44,19 +44,20 @@ ScintillaQt::ScintillaQt(QAbstractScrollArea *parent)  	WndProc(SCI_SETBUFFEREDDRAW, false, 0);  	Initialise(); + +	for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) { +		timers[tr] = 0; +	}  }  ScintillaQt::~ScintillaQt()  { -	SetTicking(false); +	for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) { +		FineTickerCancel(tr); +	}  	SetIdle(false);  } -void ScintillaQt::tick() -{ -	Tick(); -} -  void ScintillaQt::execCommand(QAction *action)  {  	int command = action->data().toInt(); @@ -148,7 +149,9 @@ void ScintillaQt::Initialise()  void ScintillaQt::Finalise()  { -	SetTicking(false); +	for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) { +		FineTickerCancel(tr); +	}  	ScintillaBase::Finalise();  } @@ -396,25 +399,31 @@ void ScintillaQt::NotifyParent(SCNotification scn)  	emit notifyParent(scn);  } -void ScintillaQt::SetTicking(bool on) +/** +* Report that this Editor subclass has a working implementation of FineTickerStart. +*/ +bool ScintillaQt::FineTickerAvailable()  { -	QTimer *qTimer; -	if (timer.ticking != on) { -		timer.ticking = on; -		if (timer.ticking) { -			qTimer = new QTimer; -			connect(qTimer, SIGNAL(timeout()), this, SLOT(tick())); -			qTimer->start(timer.tickSize); -			timer.tickerID = qTimer; -		} else { -			qTimer = static_cast<QTimer *>(timer.tickerID); -			qTimer->stop(); -			disconnect(qTimer, SIGNAL(timeout()), 0, 0); -			delete qTimer; -			timer.tickerID = 0; -		} +	return true; +} + +bool ScintillaQt::FineTickerRunning(TickReason reason) +{ +	return timers[reason] != 0; +} + +void ScintillaQt::FineTickerStart(TickReason reason, int millis, int /* tolerance */) +{ +	FineTickerCancel(reason); +	timers[reason] = startTimer(millis); +} + +void ScintillaQt::FineTickerCancel(TickReason reason) +{ +	if (timers[reason]) { +		killTimer(timers[reason]); +		timers[reason] = 0;  	} -	timer.ticksToWait = caret.period;  }  void ScintillaQt::onIdle() @@ -740,3 +749,12 @@ void ScintillaQt::Drop(const Point &point, const QMimeData *data, bool move)  	DropAt(movePos, bytes, len, move, rectangular);  } + +void ScintillaQt::timerEvent(QTimerEvent *event) +{ +	for (TickReason tr=tickCaret; tr<=tickDwell; tr = static_cast<TickReason>(tr+1)) { +		if (timers[tr] == event->timerId()) { +			TickFor(tr); +		} +	} +} diff --git a/qt/ScintillaEditBase/ScintillaQt.h b/qt/ScintillaEditBase/ScintillaQt.h index ff4d65992..16bafbf7c 100644 --- a/qt/ScintillaEditBase/ScintillaQt.h +++ b/qt/ScintillaEditBase/ScintillaQt.h @@ -14,6 +14,7 @@  #include <stdlib.h>  #include <string.h>  #include <stdio.h> +#include <assert.h>  #include <ctype.h>  #include <time.h>  #include <string> @@ -88,7 +89,6 @@ signals:  	void command(uptr_t wParam, sptr_t lParam);  private slots: -	void tick();  	void onIdle();  	void execCommand(QAction *action);  	void SelectionChanged(); @@ -114,7 +114,11 @@ private:  	virtual void NotifyChange();  	virtual void NotifyFocus(bool focus);  	virtual void NotifyParent(SCNotification scn); -	virtual void SetTicking(bool on); +	int timers[tickDwell+1]; +	virtual bool FineTickerAvailable(); +	virtual bool FineTickerRunning(TickReason reason); +	virtual void FineTickerStart(TickReason reason, int millis, int tolerance); +	virtual void FineTickerCancel(TickReason reason);  	virtual bool SetIdle(bool on);  	virtual void SetMouseCapture(bool on);  	virtual bool HaveMouseCapture(); @@ -143,6 +147,8 @@ protected:  	void DragLeave();  	void Drop(const Point &point, const QMimeData *data, bool move); +	void timerEvent(QTimerEvent *event); +  private:  	QAbstractScrollArea *scrollArea; diff --git a/src/Editor.cxx b/src/Editor.cxx index a23683698..361e82803 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -1311,16 +1311,26 @@ void Editor::ShowCaretAtCurrentPosition() {  	if (hasFocus) {  		caret.active = true;  		caret.on = true; -		SetTicking(true); +		if (FineTickerAvailable()) { +			FineTickerCancel(tickCaret); +			if (caret.period > 0) +				FineTickerStart(tickCaret, caret.period, caret.period/10); +		} else { +			SetTicking(true); +		}  	} else {  		caret.active = false;  		caret.on = false; +		if (FineTickerAvailable()) { +			FineTickerCancel(tickCaret); +		}  	}  	InvalidateCaret();  }  void Editor::DropCaret() {  	caret.active = false; +	FineTickerCancel(tickCaret);  	InvalidateCaret();  } @@ -1328,6 +1338,11 @@ void Editor::CaretSetPeriod(int period) {  	if (caret.period != period) {  		caret.period = period;  		caret.on = true; +		if (FineTickerAvailable()) { +			FineTickerCancel(tickCaret); +			if ((caret.active) && (caret.period > 0)) +				FineTickerStart(tickCaret, caret.period, caret.period/10); +		}  		InvalidateCaret();  	}  } @@ -1659,6 +1674,15 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {  	view.PaintText(surfaceWindow, rcArea, rcClient, *this, vs); +	if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) { +		if (FineTickerAvailable()) { +			scrollWidth = view.lineWidthMaxSeen; +			if (!FineTickerRunning(tickWiden)) { +				FineTickerStart(tickWiden, 50, 5); +			} +		} +	} +  	NotifyPainted();  } @@ -3809,7 +3833,13 @@ void Editor::SetDragPosition(SelectionPosition newPos) {  	}  	if (!(posDrag == newPos)) {  		caret.on = true; -		SetTicking(true); +		if (FineTickerAvailable()) { +			FineTickerCancel(tickCaret); +			if ((caret.active) && (caret.period > 0) && (newPos.Position() < 0)) +				FineTickerStart(tickCaret, caret.period, caret.period/10); +		} else { +			SetTicking(true); +		}  		InvalidateCaret();  		posDrag = newPos;  		InvalidateCaret(); @@ -4033,6 +4063,12 @@ void Editor::DwellEnd(bool mouseMoved) {  		dwelling = false;  		NotifyDwelling(ptMouseLast, dwelling);  	} +	if (FineTickerAvailable()) { +		FineTickerCancel(tickDwell); +		if (mouseMoved && (dwellDelay < SC_TIME_FOREVER)) { +			//FineTickerStart(tickDwell, dwellDelay, dwellDelay/10); +		} +	}  }  void Editor::MouseLeave() { @@ -4080,6 +4116,9 @@ void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifie  	if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {  		//Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);  		SetMouseCapture(true); +		if (FineTickerAvailable()) { +			FineTickerStart(tickScroll, 100, 10); +		}  		if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))  			SetEmptySelection(newPos.Position());  		bool doubleClick = false; @@ -4176,6 +4215,9 @@ void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifie  			SetDragPosition(SelectionPosition(invalidPosition));  			SetMouseCapture(true); +			if (FineTickerAvailable()) { +				FineTickerStart(tickScroll, 100, 10); +			}  		} else {  			if (PointIsHotspot(pt)) {  				NotifyHotSpotClicked(newCharPos.Position(), modifiers); @@ -4188,6 +4230,9 @@ void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifie  					inDragDrop = ddNone;  			}  			SetMouseCapture(true); +			if (FineTickerAvailable()) { +				FineTickerStart(tickScroll, 100, 10); +			}  			if (inDragDrop != ddInitial) {  				SetDragPosition(SelectionPosition(invalidPosition));  				if (!shift) { @@ -4281,6 +4326,9 @@ void Editor::ButtonMoveWithModifiers(Point pt, int modifiers) {  	if (inDragDrop == ddInitial) {  		if (DragThreshold(ptMouseLast, pt)) {  			SetMouseCapture(false); +			if (FineTickerAvailable()) { +				FineTickerCancel(tickScroll); +			}  			SetDragPosition(movePos);  			CopySelectionRange(&drag);  			StartDrag(); @@ -4289,6 +4337,12 @@ void Editor::ButtonMoveWithModifiers(Point pt, int modifiers) {  	}  	ptMouseLast = pt; +	PRectangle rcClient = GetClientRectangle(); +	Point ptOrigin = GetVisibleOriginInMain(); +	rcClient.Move(0, -ptOrigin.y); +	if (FineTickerAvailable() && (dwellDelay < SC_TIME_FOREVER) && rcClient.Contains(pt)) { +		FineTickerStart(tickDwell, dwellDelay, dwellDelay/10); +	}  	//Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);  	if (HaveMouseCapture()) { @@ -4340,9 +4394,6 @@ void Editor::ButtonMoveWithModifiers(Point pt, int modifiers) {  		}  		// Autoscroll -		PRectangle rcClient = GetClientRectangle(); -		Point ptOrigin = GetVisibleOriginInMain(); -		rcClient.Move(0, -ptOrigin.y);  		int lineMove = DisplayFromPosition(movePos.Position());  		if (pt.y > rcClient.bottom) {  			ScrollTo(lineMove - LinesOnScreen() + 1); @@ -4414,6 +4465,9 @@ void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {  		}  		ptMouseLast = pt;  		SetMouseCapture(false); +		if (FineTickerAvailable()) { +			FineTickerCancel(tickScroll); +		}  		NotifyIndicatorClick(false, newPos.Position(), 0);  		if (inDragDrop == ddDragging) {  			SelectionPosition selStart = SelectionStart(); @@ -4530,6 +4584,66 @@ bool Editor::Idle() {  	return !idleDone;  } +void Editor::SetTicking(bool) { +	// SetTicking is deprecated. In the past it was pure virtual and was overridden in each +	// derived platform class but fine grained timers should now be implemented. +	// Either way, execution should not arrive here so assert failure. +	assert(false); +} + +void Editor::TickFor(TickReason reason) { +	switch (reason) { +		case tickCaret: +			caret.on = !caret.on; +			if (caret.active) { +				InvalidateCaret(); +			} +			break; +		case tickScroll: +			// Auto scroll +			ButtonMove(ptMouseLast); +			break; +		case tickWiden: +			SetScrollBars(); +			FineTickerCancel(tickWiden); +			break; +		case tickDwell: +			if ((!HaveMouseCapture()) && +				(ptMouseLast.y >= 0)) { +				dwelling = true; +				NotifyDwelling(ptMouseLast, dwelling); +			} +			FineTickerCancel(tickDwell); +			break; +		default: +			// tickPlatform handled by subclass +			break; +	} +} + +bool Editor::FineTickerAvailable() { +	return false; +} + +// FineTickerStart is be overridden by subclasses that support fine ticking so +// this method should never be called. +bool Editor::FineTickerRunning(TickReason) { +	assert(false); +	return false; +} + +// FineTickerStart is be overridden by subclasses that support fine ticking so +// this method should never be called. +void Editor::FineTickerStart(TickReason, int, int) { +	assert(false); +} + +// FineTickerCancel is be overridden by subclasses that support fine ticking so +// this method should never be called. +void Editor::FineTickerCancel(TickReason) { +	assert(false); +} +  void Editor::SetFocusState(bool focusState) {  	hasFocus = focusState;  	NotifyFocus(hasFocus); diff --git a/src/Editor.h b/src/Editor.h index 53da90906..d80292a0a 100644 --- a/src/Editor.h +++ b/src/Editor.h @@ -500,7 +500,13 @@ protected:	// ScintillaBase subclass needs access to much of Editor  	void Tick();  	bool Idle(); -	virtual void SetTicking(bool on) = 0; +	virtual void SetTicking(bool on); +	enum TickReason { tickCaret, tickScroll, tickWiden, tickDwell, tickPlatform }; +	virtual void TickFor(TickReason reason); +	virtual bool FineTickerAvailable(); +	virtual bool FineTickerRunning(TickReason reason); +	virtual void FineTickerStart(TickReason reason, int millis, int tolerance); +	virtual void FineTickerCancel(TickReason reason);  	virtual bool SetIdle(bool) { return false; }  	virtual void SetMouseCapture(bool on) = 0;  	virtual bool HaveMouseCapture() = 0; diff --git a/win32/ScintillaWin.cxx b/win32/ScintillaWin.cxx index e7aae8c3b..67fddd1cb 100644 --- a/win32/ScintillaWin.cxx +++ b/win32/ScintillaWin.cxx @@ -103,6 +103,8 @@  #define SC_WIN_IDLE 5001  typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT); +typedef UINT_PTR (WINAPI *SetCoalescableTimerSig)(HWND hwnd, UINT_PTR nIDEvent, +	UINT uElapse, TIMERPROC lpTimerFunc, ULONG uToleranceDelay);  // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables. @@ -183,6 +185,7 @@ class ScintillaWin :  	bool capturedMouse;  	bool trackedMouseLeave;  	TrackMouseEventSig TrackMouseEventFn; +	SetCoalescableTimerSig SetCoalescableTimerFn;  	unsigned int linesPerScroll;	///< Intellimouse support  	int wheelDelta; ///< Wheel delta from roll @@ -227,7 +230,7 @@ class ScintillaWin :  	static sptr_t PASCAL CTWndProc(  		    HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam); -	enum { invalidTimerID, standardTimerID, idleTimerID }; +	enum { invalidTimerID, standardTimerID, idleTimerID, fineTimerStart };  	virtual bool DragThreshold(Point ptStart, Point ptNow);  	virtual void StartDrag(); @@ -237,7 +240,11 @@ class ScintillaWin :  	virtual bool ValidCodePage(int codePage) const;  	virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);  	virtual bool SetIdle(bool on); -	virtual void SetTicking(bool on); +	UINT_PTR timers[tickDwell+1]; +	virtual bool FineTickerAvailable(); +	virtual bool FineTickerRunning(TickReason reason); +	virtual void FineTickerStart(TickReason reason, int millis, int tolerance); +	virtual void FineTickerCancel(TickReason reason);  	virtual void SetMouseCapture(bool on);  	virtual bool HaveMouseCapture();  	virtual void SetTrackMouseLeaveEvent(bool on); @@ -334,6 +341,7 @@ ScintillaWin::ScintillaWin(HWND hwnd) {  	capturedMouse = false;  	trackedMouseLeave = false;  	TrackMouseEventFn = 0; +	SetCoalescableTimerFn = 0;  	linesPerScroll = 0;  	wheelDelta = 0;   // Wheel delta from roll @@ -389,8 +397,10 @@ void ScintillaWin::Initialise() {  	// Find TrackMouseEvent which is available on Windows > 95  	HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll")); -	if (user32) +	if (user32) {  		TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent"); +		SetCoalescableTimerFn = (SetCoalescableTimerSig)::GetProcAddress(user32, "SetCoalescableTimer"); +	}  	if (TrackMouseEventFn == NULL) {  		// Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent  		if (!commctrl32) @@ -400,11 +410,16 @@ void ScintillaWin::Initialise() {  				::GetProcAddress(commctrl32, "_TrackMouseEvent");  		}  	} +	for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) { +		timers[tr] = 0; +	}  }  void ScintillaWin::Finalise() {  	ScintillaBase::Finalise(); -	SetTicking(false); +	for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) { +		FineTickerCancel(tr); +	}  	SetIdle(false);  #if defined(USE_D2D)  	DropRenderTarget(); @@ -912,12 +927,10 @@ sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam  			return 0;  		case WM_TIMER: -			if (wParam == standardTimerID && timer.ticking) { -				Tick(); -			} else if (wParam == idleTimerID && idler.state) { +			if (wParam == idleTimerID && idler.state) {  				SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);  			} else { -				return 1; +				TickFor(static_cast<TickReason>(wParam - fineTimerStart));  			}  			break; @@ -1319,20 +1332,34 @@ sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lPa  	return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);  } -void ScintillaWin::SetTicking(bool on) { -	if (timer.ticking != on) { -		timer.ticking = on; -		if (timer.ticking) { -			timer.tickerID = ::SetTimer(MainHWND(), standardTimerID, timer.tickSize, NULL) -				? reinterpret_cast<TickerID>(standardTimerID) : 0; -		} else { -			::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(timer.tickerID)); -			timer.tickerID = 0; -		} +/** +* Report that this Editor subclass has a working implementation of FineTickerStart. +*/ +bool ScintillaWin::FineTickerAvailable() { +	return true; +} + +bool ScintillaWin::FineTickerRunning(TickReason reason) { +	return timers[reason] != 0; +} + +void ScintillaWin::FineTickerStart(TickReason reason, int millis, int tolerance) { +	FineTickerCancel(reason); +	if (SetCoalescableTimerFn && tolerance) { +		timers[reason] = SetCoalescableTimerFn(MainHWND(), fineTimerStart + reason, millis, NULL, tolerance); +	} else { +		timers[reason] = ::SetTimer(MainHWND(), fineTimerStart + reason, millis, NULL);  	} -	timer.ticksToWait = caret.period;  } +void ScintillaWin::FineTickerCancel(TickReason reason) { +	if (timers[reason]) { +		::KillTimer(MainHWND(), timers[reason]); +		timers[reason] = 0; +	} +} + +  bool ScintillaWin::SetIdle(bool on) {  	// On Win32 the Idler is implemented as a Timer on the Scintilla window.  This  	// takes advantage of the fact that WM_TIMER messages are very low priority, | 
