[VBA] Всплывающая подсказка для сноски в Word 2013 без мышиного указателя

auto-teacher

Новый пользователь
Сообщения
25
Реакции
4
Как известно, если навести указатель мыши на слово со сноской (обычной или концевой), то сразу появляется прямоугольничек, а через секунду всплывает подсказка с текстом сноски.
При выполнении команд "следующая сноска" или "предыдущая сноска" курсор становится между сноской и словом, к которому она привязана.
Как сделать (какой макрокомандой или другим способом), чтобы подсказка появлялась после перескока к сноске без участия мыши?

Возможно, к этой теме относится вот это:
Свойство Cursor.HotSpot (System.Windows.Forms)
***********************************
Свойство Cursor.HotSpot
.NET Framework (current version)
Возвращает активную точку курсора.
Пространство имен: System.Windows.Forms
Сборка: System.Windows.Forms (в System.Windows.Forms.dll)
Синтаксис VB
Public ReadOnly Property HotSpot As Point
Значение свойства
Type: System.Drawing.Point
Объект Point, представляющий активную точку курсора.
HotSpot — Point В курсор, который взаимодействует с другими элементами на экране.
**********************************
То есть, по моим понятиям надо после скачка к сноске в точку расположения курсора перевести указатель мыши (сфокусировать что ли?). Может быть, тогда подсказка и всплывет, если она не глупая?
Или это мистика?
 
Последнее редактирование:
Здравствуйте, auto-teacher !
Добро пожаловать на SafeZone.

Да, можно попробовать написать такой хак.

Мы просто будем программно передвигать мышь в позицию сноски через SetCursorPos. А переходы между сносками сэмулируем вручную.

Я так понимаю речь о:
Переход к следующей сноске (в Word 2016).
ALT+SHIFT+>

Переход к предыдущей сноске (в Word 2016).
ALT+SHIFT+<

Вариант1)
Есть более простой способ - назначить другую горячую клавишу,
например, пусть это будет:
ALT + Ctrl + стрелка влево на цифровой клавиатуре
ALT + Ctrl + стрелка вправо на цифровой клавиатуре

тогда макрос будет иметь следующий вид:
модуль ThisDocument:
VB.NET / VBA:
Option Explicit

Private WithEvents appWord As Word.Application

Private Type RANGE_POSITION
    Left    As Long
    Top     As Long
    Width   As Long
    Height  As Long
End Type

Private Enum DIRECTION
    DIR_FORWARD
    DIR_BACKWARD
End Enum

Private Declare Function SetCursorPos Lib "user32.dll" (ByVal x As Long, ByVal y As Long) As Long

Dim oKey1 As KeyBinding
Dim oKey2 As KeyBinding

Private Sub appWord_WindowActivate(ByVal Doc As Word.Document, ByVal Wn As Word.Window)
    Call BindKey
End Sub

Private Sub Document_Close()
    On Error Resume Next
    oKey1.Clear
    oKey2.Clear
End Sub

Private Sub Document_Open()
    Set appWord = Word.Application
    Call BindKey
End Sub

Private Sub BindKey()
    With Application
        .CustomizationContext = ThisDocument
        Set oKey2 = .KeyBindings.Add( _
            KeyCode:=BuildKeyCode(wdKeyAlt, wdKeyControl, wdKeyNumeric4), _
            KeyCategory:=wdKeyCategoryCommand, _
            Command:="GotoNoteBack")
        Set oKey2 = .KeyBindings.Add( _
            KeyCode:=BuildKeyCode(wdKeyAlt, wdKeyControl, wdKeyNumeric6), _
            KeyCategory:=wdKeyCategoryCommand, _
            Command:="GotoNoteNext")
    End With
End Sub

Public Sub GotoNoteNext()
    FindNote DIR_FORWARD
End Sub
Public Sub GotoNoteBack()
    FindNote DIR_BACKWARD
End Sub

Private Sub FindNote(dir As DIRECTION)
    Dim i       As Long
 
    If ActiveDocument.Footnotes.Count = 0 And ActiveDocument.Endnotes.Count = 0 Then
        MsgBox "В документе нет сносок.", vbInformation
        Exit Sub
    End If
 
    If ActiveDocument.Footnotes.Count > 0 Then
        If dir = DIR_FORWARD Then
            For i = 1 To ActiveDocument.Footnotes.Count
                If CheckNote(ActiveDocument.Footnotes(i).Reference, dir) Then Exit Sub
            Next
        Else
            For i = ActiveDocument.Footnotes.Count To 1 Step -1
                If CheckNote(ActiveDocument.Footnotes(i).Reference, dir) Then Exit Sub
            Next
        End If
        If MsgBox("Поиск завершен. Начать заново?", vbYesNo) = vbYes Then
            Selection.Start = IIf(dir = DIR_FORWARD, 0, ActiveDocument.Content.End)
            FindNote dir
        End If
    End If
    If ActiveDocument.Endnotes.Count > 0 Then
        If dir = DIR_FORWARD Then
            For i = 1 To ActiveDocument.Endnotes.Count
                If CheckNote(ActiveDocument.Endnotes(i).Reference, dir) Then Exit Sub
            Next
        Else
            For i = ActiveDocument.Endnotes.Count To 1 Step -1
                If CheckNote(ActiveDocument.Endnotes(i).Reference, dir) Then Exit Sub
            Next
        End If
        If MsgBox("Поиск завершен. Начать заново?", vbYesNo) = vbYes Then
            Selection.Start = IIf(dir = DIR_FORWARD, 0, ActiveDocument.Content.End)
            FindNote dir
        End If
    End If
End Sub

Private Function CheckNote(RA As Range, dir As DIRECTION) As Boolean
    Dim RP      As RANGE_POSITION
    If IIf(dir = DIR_FORWARD, RA.Start > Selection.Start, RA.Start < Selection.Start) Then
        Selection.Start = RA.Start
        Selection.End = RA.Start
        Selection.Range.Select
        Call ActiveWindow.GetPoint(RP.Left, RP.Top, RP.Width, RP.Height, RA)
        SetCursorPos RP.Left, RP.Top
        CheckNote = True
    End If
End Function
Если впервые с макросами, то советую ознакомится с темой: Как создать макрос для MS Word / MS Excel

На Word 2013 / 2016 не тестировал, т.к. пока нет его у меня. Но на 2003 работает исправно.

P.S. Код не учитывает, если у вас в документе смешано обычные и концевые сноски вместе.

Вариант 2)
В принципе можно переопределить поведение стандартной комбинации клавиш через регистрацию глобального RegisterHotkey.
Но для отлова результата придётся прибегнуть к сабклассингу. А это не очень безопасно.
Чуть позже попробую накидать пример.

------------------------
Ссылки по теме:
Add footnotes and endnotes in Word - Word
Сочетания клавиш в Microsoft Word 2016 для Windows - Word
WdKey enumeration (Microsoft.Office.Interop.Word)
 

Вложения

  • Сноска3.doc
    41.5 KB · Просмотры: 1
Последнее редактирование:
Здравствуйте, Dragokas!
Спасибо за совет!
Я действительно не силен в программировании, поэтому делаю, как и многие чудики: сдуваю откуда-нибудь что-то подходящее, кумекаю по-своему над этим, и иногда мне удается даже что-то улучшить для себя.
То, что Вы мне прислали, я испытал на Вашем же доке, а потом скопировал в модуль своего шаблона, где у меня полно таблиц со сносками разными и примечаниями.
Я без комментариев в коде, наверное, не все из него понял, особенно в части, где курсор получает координаты...
При прогоне вверх и вниз есть большая разница: не всегда всплывают подсказки. При движении вниз вообще редко всплывают, а вот при движении вверх гораздо чаще, что мне понравилось. В то же время при движении вверх (назад) зачастую выделялась вся строка таблицы, где была сноска.
Еще раз повторяю: "Большое спасибо!".
Но.... Чего мне надо.
Мне не надо переопределять в макросе горячие клавиши.
Мне не надо осуществлять поиск сносок циклом ни вниз, ни вверх.
Мне не надо выделять слова со сносками.
Мне надо только одно: чтобы после перескока к сноске (и вверх, и вниз) возле нее всплыла подсказка.
До Вашего кода я пользовался другими кодами, который мне прислали с другого форума. Они тоже не до конца решали мои наглые запросы.
Может быть, я путано объяснил в теме свою проблему. Повторю по-другому.
Что имеем:
1. Я штатной командой GoToNextEndnote прыгаю к следующей сноске (и неважно чем - кнопкой на ленте или горячей клавишей).
2. Курсор, как ему и положено, становится между словом, к которому привязана сноска, и самой сноской (это ясно видно, особенно при масштабе 500%).
3. Подсказка после этого всплывать и не думает, потому что она реагирует только на указатель мыши около слова со сноской, вместе взятыми. А указатель где? Неизвестно, - шатается в другом месте.

Я подумал своими куриными мозгами: чтобы она всплывала, надо указатель мыши поставить туда, куда прыгнул курсор ввода текста. Мне помогли: заставили его туда перемещаться: и другие программисты, и Вы вот теперь. Но все равно подсказка не всегда всплывает.
И теперь мне видно, что одного лишь прыжка указателя вслед за курсором мало для всплытия подсказки.
Чует мое сердце, что после перемещения указателя мыши в точку координат курсора надо заставить этот мышиный указатель чуть-чуть шевельнуться еще на пару пикселей в магнитном поле сноски.

Об этом говорит такой факт: попробуйте щелкнуть мышью в слове со сноской. Курсор ввода встанет между знаками, указатель мыши тоже будет рядышком, а подсказки-то не будет. Она только тогда загорится, когда слегка шевельнешь мышь.
Или попробуйте с Вашими макросами: в том случае, когда она не всплывет, аккуратно сдвиньте мышь на столе на капельку - и сразу изменится указатель и всплывет подсказка.

Повторюсь: я не понял как следует смысл всех операторов в коде по части GetPoint и SetCursorPos, но почему-то думаю, что надо применить именно то свойство указателя мыши (событие, наверное), которое отвечает за его вход в поле сноски или движение над ним (может, Mouseover или out или там move).

Если Вам и дальше интересно решать эту задачу, то хотелось бы видеть это в такой форме (на примере с другого форума):

Sub СноскаСлед()
Application.Run "GoToNextFootnote"
Dim cX As Long, cY As Long
ActiveWindow.GetPoint cX, cY, 0&, 0&, Selection.Next(wdCharacter)
SetCursorPos cX, cY
' Вот здесь, мне кажется, и надо напоследок чуток шевельнуть указатель мыши!
End Sub

Спасибо! Буду ждать ответа!
 
я не понял как следует смысл всех операторов в коде по части GetPoint и SetCursorPos

auto-teacher, я к сожалению о такой команде как GoToNextFootnote не знал, поэтому и написал свой враппер.
У меня всё работает с первого раза, иначе я бы не присылал этот код.

По Вашим замечаниям, почему не появляется (не всегда появляется) подскаска, смею предположить одну из нескольких вещей:
1. Не совсем точно определяется координата точки сноски.
2. Вы в течении 1 сек. нажимаете какую-то (пусть даже вспомогательную вроде Ctrl, Alt) клавишу, которая сбивает фокус и подсказка из-за этого не появляетя.
Мне не надо переопределять в макросе горячие клавиши.
То о чём вы просите (повесить на уже существующую горячую клавишу другое действие) - и есть самое настоящее переопределение. То есть вы вместо одной стандартной функции Word, хотите другую. Такого понятия - как дополнить одну функцию другой - нет, так как горячая клавиша вызывает только что-то одно. Если мы вмешиваемся в этот процесс, то обязаны сами исправить все, что сломали -) - в вашем случае - это команда Application.Run "GoToNextFootnote". В моём - поиск вручную.
Но как уже говорил, это делается через сабклассинг, который в word-e следать сложнее, чем в обычной программе. А примеров, к сожалению, нет хороших. Я попробую позже.
Так что давайте с комбинацией чуть позже. А сейчас разберемся хотя бы с корректным появлением подсказки.

я не понял как следует смысл всех операторов в коде по части GetPoint и SetCursorPos
GetPoint - получает координату, высоту и ширину символа - цифры сноски.
SetCursorPos - перемещает указатель мыши в эту координату.

Попробуйте такой код:
VB.NET / VBA:
Option Explicit

Private Type RANGE_POSITION
    Left    As Long
    Top     As Long
    Width   As Long
    Height  As Long
End Type

Private Declare Function SetCursorPos Lib "user32.dll" (ByVal x As Long, ByVal y As Long) As Long

Sub СноскаСлед()
    Dim RP As RANGE_POSITION
    Dim cX As Long, cY As Long
    Application.Run "GoToNextFootnote"
    ActiveWindow.GetPoint RP.Left, RP.Top, RP.Width, RP.Height, Selection.Next(wdCharacter)
    cX = RP.Left + RP.Width \ 2
    cY = RP.Top + RP.Height \ 2
    SetCursorPos cX, cY
    SetCursorPos cX + 1, cY + 1
    SetCursorPos cX, cY
End Sub

и скажите улучшилось ли.

Ну и в целом можно забить на всё это баловство с мышкой и вручную отрисовывать окно с подсказкой, тогда она будет появляться не через секунду, а сразу.
 
Здравствуйте, уважаемый Dragokas!
Я чувствую, что чем дальше - тем больше путаницы.
Не могли бы Вы написать, коротенькую макрокоманду на VBA, которая заставит указатель мыши начать движение вниз и после того, как он пройдет 5-10 пикселей, прекратить движение?
 
Сначала ответьте на предыдущий вопрос.
которая заставит указатель мыши начать движение вниз и после того, как он пройдет 5-10 пикселей, прекратить движение?
В коде выше указатель устанавливается в координаты cX, cY, затем смещается на 1 пиксель вправо вниз, и возвращается обратно.
В чем проблема его протестировать и написать мне работает или нет?
 
Здравствуйте!
Я Вам благодарен, но Вы же и сами должны видеть, что это не прокатит!
Последнее значение - все равно то же: cX, cY.
Все это я пробовал, как Вы и говорили.
По SetCursorPos курсор послушно ставится: и рядом со сноской, и в слове со сноской. И может непрерывно сопровождать каретку при переходе по сноскам.
Я его даже сдвигал при помощи цикла внутрь поля, причем с временной задержкой (0.1 сек). Двигаться он двигается (и это смотрится, как анимация), но движение не воспринимается как наведение на сноску: указатель не меняет форму, и подсказка не появляется.
А в тех случаях, когда подсказка все-таки появляется, тоже неизвестно, почему они нерегулярны: вверх - появляются, вниз - нет или наоборот).
Здесь, по-видимому, надо как-то программно имитировать наведение указателя на цель (на поле слова со сноской).
 
Я Вам благодарен, но Вы же и сами должны видеть, что это не прокатит!
Так в том то и дело, что я не вижу, так у меня всё нормально работает. Поставлю новый офис, помотрю, как дела обстоят в нём.

Тогда остаётся вариант, самому отрисовать окошко с подсказкой, будет и ганрантированно, и без секундной задержки.
 
А что Вы имеете ввиду - "все нормально работает" - ?
Хотите сказать, что каждый раз при переходе к другой сноске всплывает ее подсказка?
 
Да.
Поставил Word 2013. Там действительно как-то странно работает. Буду думать.
 
Ну по 2013 есть подсказки касающиеся того, что он стал 64-х битовым, и надо по-другому пользоваться. Мне подсказывали с таким же кодом, как Ваш:

Пример актуален только для 32-bit. Если Вы являетесь обладателем 64-bit, то Вам необходимо изменить об'явление WinAPI функции SetCursorPos и тип переменных cX и cY. Сделать это поможет статья, опубликованная на официальном сайте Microsoft.
Совместимость 32- и 64-разрядных версий Office 2010 и файл Win32API_PtrSafe.txt, который можно скачать там же.


Но в данном случае нет же никаких громадных расчетов, чтобы не поместились в 32 бита?
 
Добавил цикл по таймеру и для себя проблему решил.
Подсказки появляются при переходе по ссылкам и сноскам во всех режимах просмотра.
Если не появится решения с применением событий мыши (наведение и пр.), можно тему закрыть.
 
Вопросы и про всплывающие подсказки, и про цикл с условием, где Вы участвовали, - преследовали одну цель.

Задача стояла такая.
Как известно, в Word'е штатная команда GoToNextFootnote (и другие такие же) находит следующую сноску. Курсор в этом случае становится перед знаком сноски (это особенно хорошо видно при большом масштабе - 500%. Кстати, так же происходит и с гиперссылками).
Для того, чтобы к этой сноске всплыла подсказка, надо навести на нее указатель мыши.
Об этом все, конечно, знают.

У меня в доках много ссылок, сносок и примечаний.

Мне захотелось мобильности, а именно:

1. Чтобы после перехода к след. сноске по команде GoToNextFootnote сразу же всплывала ее подсказка, а не дожидалась наведения указателя мыши.
Я искал на форумах, как это сделать, но смогли только подсказать, как найти координаты курсора и переместить на них указатель мыши.
Подсказка от этого не хотела всплывать. Она надрессирована лишь на наведение мыши. Что было делать? Надо было шевельнуть мышь.
Вот я ее как мог и подвинул: по таймеру велел сместиться на три пикселя вниз и вправо. Оказалось, что эта мультипликация подействовала: подсказки всплывают хорошо.
Другого способа мне не посоветовали.
Я знаю, что есть там какие-то события мыши... Но не знаю, как воспользоваться ими.

2. Есть также команда ViewFootnoteArea, которая перемещает курсор туда-сюда между знаками сноски в основном тексте и знаками сноски в тексте сносок. Но в основном тексте подсказка бывает, а в тексте сносок ее нет. Поэтому понадобилось условие, которое Вы мне подсказали.

Все это имеет эффект только в том случае, если для указанных команд назначены удобные горячие клавиши. У меня они очень удобные (буквы условно русские):

ПредСноска Alt+Щ ПредКонцевая Alt+З
СноскиПростые Alt+Д СноскиКонцевые Alt+Ж
СледСноска Alt+Ю СледКонцевая Alt+.

Продолжение следует...
В результате работы форума и его энтузиастов, помогавших мне, я теперь имею хороший набор макросов для работы со сносками и гиперссылками!

Кстати, Dragokas, я и до Вас пытался указатель мыши как бы пошевелить на пару пикселей после того, как дал ему координаты курсора. Но как и Вашем совете сначала было не так, как надо: не чувствовалось движения. Потом я применил цикл по таймеру, и вышла некая мультипликация, которая заработала, как ни странно.

Дальше идет пример, похожий на Ваш вот этот:
VB.NET / VBA:
Sub СледСноска()
    If Selection.Information(wdInFootnote) then
        msgbox "Операторы 1"
    Else ' Если курсор в основном тексте
        msgbox "Операторы 2"
    End If
End Sub
VB.NET / VBA:
Sub СледСноска()
Dim cX As Long, cY As Long, i As Byte
On Error Resume Next
On Error GoTo 0
If Selection.StoryType = wdFootnotesStory Then ' Если курсор в области простых сносок, подсказки в этой области не будет, а просто будет перескок к след. сноске
    Application.Run "GoToNextFootnote"
Else ' Следовательно, подразумевается, что курсор в основном тексте, - и, значит, подсказка должна всплыть
    Application.Run "GoToNextFootnote"
        ActiveWindow.GetPoint cX, cY, 0&, 0&, Selection.Range
            For i = 0 To 2 ' Цикл для создания мультипликации указателю мыши
                SetCursorPos cX + i, cY + i
                Dim Start
                Start = Timer ' текущее время в секундах
                Do While Timer < Start + 0.05
                Loop
            Next i
End If
End Sub
Цикл придумал эмпирически. Отказов почти нет, по крайней мере в режиме разметки.
В итоге имею такой макрос. Он хорошо работает, но что в нем лишнего или бесполезного я уж теперь фиг пойму.
VB.NET / VBA:
Sub СледКонцеваяСноска()
' С условиями перехода в зависимости от области текста и сообщением о последней найденной концевой сноске
Dim cX As Long, cY As Long, i As Byte
Dim en As Endnote
Dim en_cnt As Long
Dim enr As Range
en_cnt = ActiveDocument.Endnotes.Count
On Error Resume Next
On Error GoTo 0
If Selection.StoryType = wdEndnotesStory Then ' Если курсор в области концевых сносок, после перехода подсказки не ожидается
    Application.Run "GoToNextEndnote"
Else ' Подразумевается, что курсор в основном тексте, - подсказка должна всплыть, как дрессированный дельфин
If en_cnt > 0 Then
    Application.Run "GoToNextEndnote"
        ActiveWindow.GetPoint cX, cY, 0&, 0&, Selection.Range
            For i = 0 To 2 ' Цикл для создания мультипликации указателю мыши
                SetCursorPos cX + i, cY + i
                Dim Start
                Start = Timer ' текущее время в секундах
                Do While Timer < Start + 0.05
                Loop
            Next i
    Set enr = Selection.Range
    For Each en In ActiveDocument.Endnotes
        If en.Reference.Start = enr.Start Then
            If en.Index = en_cnt Then
                MsgBox "Хватит искать! Это последняя концевая сноска в документе!"
            End If
        End If
    Next
End If
End If
End Sub
 
Последнее редактирование модератором:
auto-teacher, умно. Ну а я проделал почти тоже самое без паузы и не сработало -)) А то, что задержка поможет, не поверил.

В коде всё нормально. Разве что выравнивание чуть скосилось. Все End if должны быть на уровне соответствующих им If.
Код:
On Error Resume Next
On Error GoTo 0
Первая делает безусловное игнорирование ошибок (что плохая практика для программистов и используется только в специфических случаях).
А вторая строка отменяет первую :) т.е. обе можно удалить.
VB.NET / VBA:
    For Each en In ActiveDocument.Endnotes
        If en.Reference.Start = enr.Start Then
            If en.Index = en_cnt Then
                MsgBox "Хватит искать! Это последняя концевая сноска в документе!"
            End If
        End If
    Next
Тут можно без цикла, как вы уже наверное догадались по соседней теме.
Просто сразу:
VB.NET / VBA:
    'берем последнюю сноску
    set en = ActiveDocument.Endnotes(ActiveDocument.Endnotes.Count)
    If en.Reference.Start = enr.Start Then
        MsgBox "Хватит искать! Это последняя концевая сноска в документе!"
    end if
 
Dragokas!

Сказать про этот фокус - "умно" можно только в шутку. Как-то еще я этот цикл сдул где-то и сумел вставить в нужное место.

Я вот читаю то, что ниже, и все пытаюсь понять, можно ли не катая мышь по столу, сделать желаемый мною сдвиг указателя?

Из Справки (Событие MouseMove):

Событие MouseMove применяется к формам, элементам управления на форме и меткам.

События MouseMove создаются непрерывно при перемещении указателя мыши по объектам. Если только мышь не отслеживается другим объектом, объект распознает событие MouseMove, когда положение мыши находится в его границах.

Перемещение формы также может создавать событие MouseMove, даже если мышь находится в неподвижном состоянии. События MouseMove создаются при перемещении формы под указателем. Если макрос или процедура события перемещают форму в ответ на событие MouseMove, это событие может непрерывно вызывать события MouseMove.

Ведь шарик мыши (если говорить о первых мышках) что делает? Он крутит колесики, оси которых перепендикулярны. Как там все передается с этих колесиков на указатель, я не знаю и не понимаю. Но сделать-то хочется!

Почему-то я верю, и меня эта фраза и возбуждает: может создаваться "событие MouseMove, даже если мышь находится в неподвижном состоянии".
Поищите, может, создадите движение нормальным, а не суррогатным способом.
Ведь в принципе идея хорошая? Я, например, очень рад, что хоть до этого дошло дело. Сижу теперь, сносочки-ссылочки вставляю, просматриваю, редактирую. Мне нравится.
 
Я понимаю о чём вы. Не возбуждать событие, а вызвать его процедуру напрямую, вот только она может быть приватная и недоступна извне.
Спрошу помощи у зарубежных коллег. Посмотрим, что они придумают.
 
Последнее редактирование:
Назад
Сверху Снизу