Підказки ToolTips в діалогових вікнах MFC, C / C + +, Програмування, статті

Пол Кнаплунд

Підказки ToolTips істотно спрощують процес освоєння прикладної програми. В даній статті розповідається, як реалізувати такі підказки в діалогових вікнах і на сторінках параметрів.

ToolTips – зручний механізм, що дозволяє виводити на екран короткі відомості про кнопки інструментальної панелі в додатках Microsoft Visual C / MFC. Користувачеві не обов’язково навіть знати про те, як застосовувати це засіб: підказка з’являється автоматично при установці курсора на відповідній кнопці. Безперечно, ToolTips – корисний засіб, але лише до певної міри. Але чи не можна призначати підказки не тільки для інструментальної лінійки, але і для інших ділянок прикладної програми?

У даній статті ми розглянемо питання про те, як реалізується механізм ToolTips в діалогових вікнах і на сторінках параметрів. Попутно ми коротко познайомимося з тим, як реалізовані в MFC стандартні (використовувані за замовчуванням) підказки ToolTips. Детально, крок за кроком, ми на конкретному прикладі досліджуємо процедуру реалізації цих підказок в діалоговому вікні і, нарешті, утворюємо нові похідні класи, здатні працювати з ToolTips, що допоможе нам в майбутньому знаходити більш елегантні програмні рішення.

MFC ToolTips

Реалізація підказок ToolTips для кнопок інструментальної панелі засобами MFC не складає труднощів, тому що основна частина роботи вже зроблена за нас. Щоб розібратися в тому, як працює механізм ToolTips, давайте підготуємо невеликий проект із застосуванням цих підказок. Клацніть клавішею миші на команді File / New у вікні Developer Studio, потім – на закладці Projects і виділіть MFC App-wizard (exe). Введіть слово ToolTips в поле імені проекту і клацніть на кнопці OK. Виберіть варіант Single Document (“Один документ”), після чого натисніть на кнопку Next. Для всіх наступних етапів задавайте параметри, прийняті за замовчуванням, і на закінчення клацніть на кнопці Finish. Проект ToolTips готовий.

Після виконання цього завдання клацніть на закладці Resources робочої області проекту. Подвійним клацанням на ресурсі інструментальної панелі IDR_MAINFRAME викличте панель інструментів і двічі клацніть на крайній зліва кнопці. На екрані з’явиться діалогове вікно Toolbar Button Properties (Властивості кнопок інструментальної лінійки) з ідентифікатором нового файлу ID_FILE_NEW. Зверніть увагу на текст “Create a new document \ nNew “в поле запрошення (Prompt). Тут ми бачимо текст, який відображається в рядку стану, роздільник” \ n “і текст підказки ToolTip. Для зміни вмісту підказки ToolTip слід внести зміни в текст, наступний за роздільником “\ n” в полі Prompt.

“Майстер” AppWizard не тільки надає ресурси, необхідні для реалізації ToolTips, але і дозволяє використовувати ці ресурси в процесі формування вікна-рамки. Це відбувається при виконанні функцією CMainFrame :: OnCreate виклику m_wndToolBar.SetBarStyle. Головним “дійовою особою” тут стає параметр стилю CBRS_TOOLTIPS, переданий функції SetBarStyle. Ця функція автоматично викликає EnableToolTips (True), в результаті чого і запускається механізм ToolTips. Правда, тут потрібно відзначити ще одну тонкість. MFC генерує запит на виведення підказки лише після того, як приходить до висновку (зокрема, за допомогою таймера), що підказка повинна бути виведена на екран. Важливу роль при цьому відіграє призупинення руху миші. Якщо курсор швидко переміщається по кнопках, нічого не відбувається. Але коли миша завмирає, програма формує повідомлення TTN_NEEDTEXT, що запитує текст підказки ToolTip. MFC вирішує цю проблему за допомогою функції CFrameWnd :: OnToolTipText (), яка витягує текст підказки з таблиці рядків.

Таким чином, процес реалізації механізму ToolTips складається з трьох базових етапів:

* Підготовка рядка, що містить текст підказки ToolTip;

* Формування вікна для ToolTips;

* Обробка запитів на виведення тексту підказки.

ToolTips в діалогових вікнах

Тепер давайте організуємо, з урахуванням згаданих трьох етапів, висновок підказок ToolTips в простому діалоговому вікні. При цьому ми скористаємося діалоговим вікном IDD_ABOUTBOX, яке “майстер” AppWizard сформував для нас при створенні проекту ToolTip.

Для редагування діалогового вікна IDD_ABOUT звернемося до редактора ресурсів. Замінимо текст одного з елементів управління статичним текстом на “Telephone”, а текст другого елементу видалимо. Створимо поле редагування з ідентифікатором IDC_ABOUT_TELEPHONE. Після цього діалогове вікно має виглядати приблизно так, як показано на рис. 1.

Тепер займемося редагуванням таблиці рядків. Двічі клацніть на ресурсі таблиці рядків і натисніть на клавішу Ins. В якості ідентифікатора введіть IDC_ABOUT_TELEPHONE, а в якості заголовка – Enter # # # – # # # – # # #. Закрийте діалогове вікно властивостей рядків. Ми тільки що створили текст підказки ToolTip, який буде виводитися для Поля редагування Telephone.

Тепер запустіть “Майстер” ClassWizard, натиснувши на комбінацію клавіш Ctrl-W. Як ім’я класу і ідентифікатора об’єкта вкажіть CAboutDlg. Прокрутіть вниз список повідомлень і виділіть WM_INITDIALOG. Клацніть на елементі Add Function, а потім – на Edit Code. Натисніть на клавішу Del, щоб прибрати коментарі, і введіть текст Enable ToolTips (TRUE); натисніть Enter.

Ми завершили виконання перших двох етапів роботи. Виконайте компіляцію програми і переконайтеся, що в отриманому початковому тексті немає помилок. Тепер нам належить зайнятися повідомленням TTN_NEEDTEXT. На жаль, в списку готових повідомлень “майстра” ClassWizard такого повідомлення немає, тому створювати його нам доведеться “вручну”.

Для цього потрібно двічі клацнути на елементі CAboutDlg закладки Class View робочої області проекту. На екрані з’явиться заголовок класу CAboutDlg. Знайдіть у списку функцію DECLARE_MESSAGE_MAP () і відкрийте перед нею порожній рядок, в яку потрібно ввести текст:


BOOL OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT*);

У закінченому вигляді опис повідомлення повинно виглядати так:


//{{AFX_MSG(CAboutDlg) virtual BOOL OnInitDialog(); //}} AFX_MSG
BOOL OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT*);
DECLARE_MESSAGE_MAP()

Трохи нижче в описі класу CAboutDlg знайдіть функцію END_MESSAGE_MAP () і вставте перед нею рядок. В цей рядок введіть наступний текст:


ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTipText);

Після цього опис повідомлення повинно виглядати так:


BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)//}}
AFX_MSG_MAPON_NOTIFY_EX(TTN_NEEDTEXT, 0,OnToolTipText);
END_MESSAGE_MAP()

Останній етап нашої роботи – створення OnToolTipText, що належить функції класу CAboutDlg. Для цього потрібно ввести наступний текст:


BOOL CAboutDlg::OnToolTipText(UINT,NMHDR* pNMHDR, LRESULT*)
{ TOOLTIPTEXT* pTTT = (TOOLTIPTEXT*)pNMHDR;
if(!(pTTT->uFlags & TTF_IDISHWND)) / / ID повинен являти собою дескриптор вікна
return FALSE;
UINT uID = pNMHDR->idFrom;
uID = ::GetDlgCtrlID((HWND) uID);
CString ctrTipText; if(!ctrTipText.LoadString(uID)) / / Завантажуємо ресурс String
return FALSE; strncpy(pTTT->lpszText,strTipText, 80); / / Передаємо текст в / / Tool tip, макс. довжина 80 знаковreturn TRUE; / / Повідомлення / / Опрацьовано
}

Розглянемо цей текст більш докладно.

Рядок 1 функції OnToolTipText перетворює параметр pNMHDR в те, чим він насправді і є, – покажчик на структуру TOOLTIPTEXT. Ця структура містить два елементи, що представляють для нас інтерес: елемент uFlags, який показує, заданий чи дескриптор вікна, і елемент lpszText, який містить текст виведеної на екран підказки.

Рядок 2 перевіряє елементи uFlags структури TOOLTIPTEXT на наявність дескриптора вікна. Якщо дескриптор не заданий, програмі передається значення FALSE і підказка ToolTip не виводиться на екран. Цей захід безпеки – Спадщина вихідної функції CFrameWnd :: OnToolTipText (), якій доводилося обробляти великий набір ресурсів, у тому числі меню, кнопки і роздільники меню.

Рядки 4 та 5 витягують дескриптор вікна HWND зі структури NMHDR і перетворять його в ідентифікатор елемента керування діалогового вікна, в який ми, працюючи з таблицями рядків, поміщали текст підказки ToolTip. Рядок 7 завантажує цей текст в об’єкт CString, а рядок 9 копіює перші 80 знаків підказки в елемент lpszText структури TOOLTIPTEXT (максимальна довжина підказки ToolTips становить 80 знаків). Остання рядок передає програмі значення TRUE, сповіщаючи її про те, що підказка коректна і може бути відображена на екрані.

Тепер приступимо до реалізації проекту. Ви повинні переконатися, що в тексті немає жодної помилки і MFC не генерує жодного попередження. Запустіть програму на виконання і перевірте роботу діалогового вікна About ToolTip. Залишаючи над полем введення Telephone. На екрані спливе підказка (рис. 2).

Об’єктно-орієнтоване рішення

Вищеописане рішення добре працює з одним діалоговим вікном. А якщо у вашій програмі десятки таких вікон? Щоб вбудувати засоби підказки в кожне діалогове вікно, вам доведеться багато разів повторювати операції вирізання та вставки. Неважко уявити, що станеться, якщо в логіку вкрадеться помилка або ви захочете внести якісь зміни після того, як виконаєте всі операції вирізання та вставки.

Набагато зручніше створити для роботи з підказками новий клас, властивості якого могли б наслідувати класи діалогів. Якщо вам ще не доводилося створювати власні класи в MFC, наше вправа буде нескладним введенням в цей процес. Ну, а якщо ви досвідчений програміст, вам буде приємно освіжити в пам’яті деякі деталі.

Ми створимо новий клас CTTDialog для обробки всіх функцій, пов’язаних з підказками ToolTip. Необхідні для формування класу CTTDialog дані ми скопіюємо з визначення класу CAboutDlg, попутно коментуючи наші дії. Перш за все створимо новий клас за допомогою “майстра” ClassWizard. Запустіть ClassWizard натисканням клавіш Ctrl-W і клацніть на елементі Add Class | New. Введіть ім’я CTTDialog в якості назви базового класу. Натисніть на кнопку OK. Якщо ClassWizard повідомить, що ідентифікатор ресурсу йому невідомий, клацніть на OK. Цю проблему ми вирішимо трохи пізніше, а поки закрийте вікно ClassWizard клацанням на кнопці OK.

У робочій області проекту виберіть закладку Class View і двічі клацніть на класі CTTDialog, після чого відкриється файл TTDialog.h. Внесіть зміни в конструктор CTTDialog (), щоб він прийняв наступний вид:

 CTTDialog (UINT uID. CWnd * pParent = NULL); / / конструктор Нестандартний!

Користувальницький клас діалогів (в даному випадку CAboutDlg) передає вбудований ідентифікатор ресурсу (IDD_DIALOG) конструктору CDialog. Ми приймаємо цей ідентифікатор в якості першого параметра для конструктора CTTDialog. Тепер потрібно клацнути на класі CAboutDlg у вікні Class View і потім вирізати і переслати в буфер обміну наступний рядок:


BOOL OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT*);

Поверніться у вікно файлу TTDialog.h натисканням комбінації клавіш Ctrl-Tab. Вставте цей рядок в ту ж позицію файлу TTDialog.h, безпосередньо перед макросом DECLARE_MESSAGE_MAP. А заодно видаліть рядок { IDD = _UNKNOWN_RESOURCE_ID_}, якщо ClassWizard використовував її при формуванні попередження на першому етапі роботи над проектом.

Поверніться у вікно CAboutDlg, перейдіть на початок файлу і введіть директиву # include “TTDialog.h” в те місце, де вміщено інші директиви # include. Прокрутіть по файлу вниз за допомогою клавіші “Page down” і замініть ім’я класу, який породив CAboutDlg, на CTTDialog. Опис породження цього класу має виглядати так:


class CAboutDlg : public CTTDialog

Знайдіть в описі класу CAboutDlg наступний рядок і виріжте її:


ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTipText)

Відкрийте файл TTDialog.cpp і вставте цей рядок в опис повідомлення. Перейдіть у вікно CAboutDlg і цілком виріжте звідти опис функції OnToolTipText. А тепер поверніться до файлу TTDialog.cpp і вставте функцію в кінці файлу. На початку опису функції OnToolTipText замініть ім’я класу CAboutDlg на CTTDialog. Знову перейдіть у вікно CAboutDlg і виріжте рядок EnableToolTips (TRUE); з опису функції OnInitDialog ().

Запустіть “майстер” класів ClassWizard, в якості імені класу вкажіть CTTDialog, виділіть повідомлення WM_INITDIALOG і клацніть на Add Function. Після цього клацніть на елементі управління Edit Code (редагування тексту) і вставте рядок EnableToolTips (TRUE); в опис функції CTTDialog :: InitDialog (). Прокрутіть в початок файлу TTDialog.cpp. Внесіть зміни в конструктор CTTDialog :: CTTDialog, щоб надати йому такий вигляд:


CTTDialog::CTTDialog(UINT uID. CWnd* dParent /*=NULL*/)
: CDialog(uID, pParent)

Тіло функції залишається порожнім. Закрийте файл TTDialog.cpp, зберігши внесені до нього зміни. Поверніться у вікно CAboutDlg і замініть в описі цього класу всі посилання на CDialog посиланнями на CTTDialog. Збережіть зміни і закрийте файл.

Тепер перекомпонуем наш проект. Якщо вами були допущені помилки, уважно, етап за етапом, перевірте правильність своїх дій. Випробуйте нову програму. Підказки ToolTips повинні з’являтися в поле Telephone так само, як і раніше.

Заради “спортивного інтересу” додайте рядок для ідентифікатора кнопки OK (IDOK). В якості її змісту можна використовувати слова “Save and Exit” (“Збережіть зміни і закрийте програму”) або що-небудь в цьому роді. Скомпонуйте проект заново. Тепер при установці курсора на кнопці OK вікна About Dialog на екрані повинен спливати підказка “Save and Exit”.

Нарешті прийшов час скористатися плодами зусиль, витрачених на підготовку класу CTTDialog. Зараз ви зможете переконатися, що вдруге все робиться дуже просто. Давайте створимо нове діалогове вікно, “оснащене” засобами роботи з ToolTips. Перш за все введіть у ваш проект новий діалоговий ресурс. Клацніть на Insert | Resource, виділіть пункт Dialog і клацніть на елементі New. У проект вноситься новий діалоговий ресурс IDD_DIALOG1. Запустіть ClassWizard і створіть новий клас. Дайте йому ім’я CDialog1 і клацніть на екранній кнопці OK. Закрийте ClassWizard повторним клацанням на цій кнопці.

Відкрийте файл Dialog1.cpp і всі згадки класу CDialog замініть на CTTDialog (таких згадок повинно бути три). Закрийте файл CDialog1.cpp, зберігши внесені до нього зміни. Відкрийте файл Dialog1.h і замініть ім’я класу CDialog на CTTDialog (тут воно згадується один раз). Перед описом породження цього класу в CDialog.h введіть директиву # include “TTDialog.h”. Збережіть зміни і закрийте файл Dialog1.h. Для завершеності внесіть в таблицю рядків запис IDCANCEL для ідентифікатора кнопки Cancel зі змістом “Cancel and Exit”.

В редакторі ресурсів введіть в меню View команду Display Dialog1. За допомогою “майстра” класів ClassWizard задійте цю команду в класі CMainFrame і виведіть опис класу CDialog1 в модальному режимі. Тепер відкрийте файл MainFrm.cpp і додайте директиву # include “Dialog1.h”. Розташуйте її на початку файлу поряд з іншими директивами # include. Перейдіть до функції CMainFrame :: OnViewDialog1 (). Замість наявних коментарів введіть в тіло функції наступні рядки:


CDialog1 dlg;dlg.DoModal();

Перекомпонуйте проект і протестуйте програму. У новому діалоговому вікні мають з’явитися підказки ToolTip для кнопок OK і Cancel.

При роботі з класом CTTDialog включення коштів підказок ToolTip в діалогове вікно не складає труднощів. Спочатку відповідно до звичайної процедури створюється діалогове вікно. Потім для кожного елемента управління вводяться унікальні для даного вікна записи таблиці рядків. Все спільно використовуються ідентифікатори (IDOK, IDCANCEL і т. д.) вводяться в таблицю рядків тільки один раз. Далі потрібно ввести директиву # Include “TTDialog.h” на початку заголовки і, відшукавши в цьому файлі і файлі реалізації діалогового вікна всі посилання на клас CDialog, замінити їх посиланнями на CTTDialog. І останнє: якщо ви використовуєте функцію OnInitDialog, не забудьте внести зміни в посилання на базовий клас. Функція повинна посилатися на CTTDialog.

Деякі пояснення

* Статичний текст. Як правило, в роботі зі статичним текстом підказки ToolTip не потрібні. З цієї посилки виходили розробники MFC, і саме з цієї причини програма не направляє сповіщень TTN_NEEDTEXT елементам управління з ідентифікатором IDC_STATIC. Якщо ви хочете доповнити засобами ToolTips елементи управління статичним текстом, вам слід замінити ідентифікатор IDC_STATIC яким-небудь іншим. Робота з текстом підказок у таблиці рядків здійснюється так само, як і у випадку з іншими елементами управління.

* Комбіновані вікна – прості і з спадаючим списком. Це вже завдання для досвідчених програмістів. Якщо ви одержимі ідеєю побудови абсолютно бездоганного інтерфейсу, вам буде неприємно дізнатися, що кошти ToolTips не працюють з комбінованими вікнами двох типів з трьох можливих. Проблема полягає в необхідності підготовки “додаткових” елементів управління для формування простих комбінованих вікон і вікон з спадаючими списками. Коли ви організуєте комбіноване вікно в редакторі ресурсів, воно фактично стає “предком” другого елемента управління – елемента, що виводить на екран список записів комбінованого вікна.

На щастя, ця проблема цілком вирішувана: потрібно скористатися функцією зворотного виклику EnumChildWindows. В рамках даної статті, на жаль, немає можливості зупинитися на цьому детальніше, так що читачеві залишається самому розібратися в цьому в якості додаткового вправи.

Сторінки параметрів

Той же об’єктно-орієнтований підхід, який ми застосовували при роботі з діалоговими вікнами, докладемо і до сторінок параметрів. Такі сторінки створюються в редакторі ресурсів аналогічно тому, як формуються діалогові вікна. Ви розміщуєте на екрані елементи управління, а потім за допомогою “майстра” класів ClassWizard створюєте новий клас для сторінки. В якості базового класу в цьому випадку використовується не CDialog, а CPropertyPage. Процес побудови сторінок параметрів і списків параметрів досить докладно описаний в документації до MFC, тому тут я не буду торкатися цієї теми.

Як і в попередніх прикладах, для реалізації засобів ToolTip ми створимо клас CTTPropertyPage, скориставшись для цього “майстром” ClassWizard. Привласніть нового класу назву CTTPropertyPage; в якості базового класу ми будемо використовувати CPropertyPage. Клацніть на кнопці OK, якщо ClassWizard виведе на екран попередження про відсутність діалогового ресурсу для даного класу. Створіть функцію для повідомлення WM_INITDIALOG і клацанням на кнопці OK закрийте вікно ClassWizard.

Використовуючи клас CTTDialog як вихідний, ми будемо вставляти взяті з його визначення рядки у визначення класу CTTPropertyPage. Хочу поділитися з вами одним прийомом, дуже, на мою думку, корисним для копіювання даних з файлу в файл. Закрийте всі відкриті вікна, виберіть закладку Class View і клацніть двічі на CTTDialog і на CTTPropertyPage. Потім з меню Windows виберіть Tile Vertically (Розмістити пліч-о-пліч). Якщо ви виконали всі ці операції, на екрані у вікні Developer Studio з’являться розташовані поруч файли TTDialog.h TTPropertyPage.h.

Під першим конструктором файлу TTPropertyPage.h розмістити другий конструктор. Він повинен мати наступний вигляд: CTTPropertyPage (UINT uID); (у файлі TTPropertyPage.h залиште текст конструктора, передбачений за замовчуванням). Пропустіть кілька рядків, і якщо в файлі TTPropertyPage.h вам зустрінеться рядок enum {IDD = _UNKNOWN_RESOURCE_ID_};, то видаліть її.

Пропустивши кілька рядків у файлі TTDialog.h, ви виявите рядок BOOL OnToolTipText (UINT, NMHDR * pNMHDR, LRESULT *);, скопіюйте її та вставте у файл TTPropertyPage.h на ту ж позицію. Збережіть зміни в файлах TTPropertyPage.h і TTDialog.h і закрийте їх.

Відкрийте файл TTDialog.cpp і розмістіть його поряд з файлом TTPropertyPage.cpp. В останньому файлі скопіюйте передбачений за замовчуванням конструктор, після чого вставте його назад – але вже під першим примірником конструктора. У першому конструкторі видаліть параметр CTTPropertyPage :: IDD з виклику базового класу CPropertyPage так, щоб цей виклик не мав параметрів. Крім того, видаліть з текст першого конструктора три рядки, що починаються з / / {{AFX_DAT_INIT (CTTPropertyPage). У другому конструкторі змініть визначення функції так, щоб воно набуло такого вигляду:


CTTPropertyPage::CTTPropertyPage(UINT uID) : CPropertyPage(uID)

Після завершення всіх цих операцій рядки повинні виглядати так:


CTTPropertyPage::CTTPropertyPage() : CPropertyPage(){}
CTTPropertyPage::CTTPropertyPage(UINT uID) : CPropertyPage(uID)
{ //{{AFX_DATA_INIT(CTTPropertyPage) / / Примітка: ClassWizard додасть сюди оператор ініціалізації належить функції
//}}AFX_DATA_INIT
}

Знайдіть у файлі TTDialog.cpp опис повідомлення ON_NOTIFY_EX (TTN_NEEDTEXT, 0, OnToolTipText) і скопіюйте його в файл TTPropertyPage.cpp. Скопіюйте рядок EnableToolTips (TRUE); з CTTDialog :: OnInitDialog () в CTTPropertyPage :: OnInitDialog (). Нижче в тексті знайдіть опис функції CTTDialog :: OnToolTipText і скопіюйте його в кінець файлу TTPropertyPage.cpp. У цьому файлі замініть ім’я CTTDialog на CTTPropertyPage.

Щоб упевнитися, що в тексті програми немає друкарських помилок, виконайте компіляцію проекту.

Отже, допоміжна сторінка CTTPropertyPage готова. Ви можете використовувати її для складання будь-яких сторінок параметрів. Внесіть зміни в механізм наслідування класу CTTPropertyPage, замініть всі посилання на CPropertyPage посиланнями на CTTPropertyPage і помістіть директиву # include TTPropertyPage в початок заголовки. Важливо пам’ятати, що якщо ви хочете реалізувати підказки ToolTips для кнопок OK, Cancel і Apply, розташованих під списком параметрів, ви повинні доповнити клас PropertySheet функцією EnableToolTips і задіяти повідомлення TTN_NEEDTEXT.

Оригінальний текст для даної статті ви можете завантажити з Web-сервера журналу PC Magazine. Користувачі хочуть (і навіть вимагають!) Отримувати такі програми, які можна освоювати не заглядаючи в друковані керівництва. ToolTips – один з кращих засобів для розробки саме таких програм.

Про автора:

Пол Кнаплунд – Президент фірми PTEX Systems, що спеціалізується на розробці програм для застосувань в області медицини. Його кар’єра розробника Windows-програм почалася в 1984 р. з випуску бета-версії ОС Windows 1.0.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*