Очень долго я искал для себя маленькую записную книжку для хранения важной информации на компьютере, которая была бы на моем рабочем столе на работе и дома. В Windows 7 есть даже специальная программка, которая называется Sticky Notes, она всем хороша, но имеет три существенных (для меня) минуса:
- Она не имеет встроенной синхронизации с несколькими компьютерами (точнее сделать то ее можно, через тот же DropBox, но с бубном);
- Она постоянно висит в панели задач в списке открытых окон;
- При нажатии на Ctrl+D (свернуть все окна), она сворачивается, как и любое другое окно. Что не очень удобно.
Также в сети была найдена программа Evernote Sticky Notes. Она также имеет из недостатков два последних пункта, но позволяет через учетную запись Evernote производить синхронизацию. После не продолжительного использования, я от нее решил отказаться.
Итак, что же делать?
Сначала покажу, что у меня получилось в итоге. И опишу основные возможности, затем как все это было сделано.
Внешний вид гаджета: | А вот страница настроек: |
Основные возможности:
- Заметки сделаны в виде гаджета для боковой панели Windows 7;
- Синхронизация хранимых данных через DropBox между несколькими компьютерами;
- Возможность изменения цвета заметки, шрифта, размера шрифта;
- Изменение размеров заметки с помощью мышки;
Реализация гаджета
Я решил сделать гаджет для Windows, который полностью бы удовлетворял меня во всех отношениях.
Механизм гаджетов в Windows позволяет не мешаться в панели задач, на них не распространяется сворачивание всех окон, они все равно будут занимать гордое место на рабочем столе. А если хранить файл с текстом заметки в папке DropBox, то можно добиться синхронизации записок на нескольких компьютерах.
Дабы избежать вопросов почему выбран именно DropBox, отвечу сразу: он мне нравится, и я его постоянно использую, а также определить папку, куда DropBox устанавливается по умолчанию достаточно просто: эта папка обычно находится здесь (на JavaScript):
System.Environment.getEnvironmentVariable("USERPROFILE") + "\DropBox\"
Алгоритм работы гаджета
Алгоритм работы гаджета очень простой.
Гаджет хранит путь к файлу, в котором содержится текст с отображаемой информацией. Этот файл находится в DropBox. При изменении текста в гаджете, измененная информация записывается в файл. DropBox после изменения файла сам синхронизирует данные между компьютерами, это уже головная боль DropBox.
Время от времени с помощью JavaScript, происходит проверка, не изменилась ли дата последнего изменения файла с информацией, которую отображает гаджет и реальным файлом. Если дата изменилась, то загружается обновленный текст (это значит, что текст гаджета был изменен на другом компьютере, либо после включения компьютера DropBox не успел обновить файл с текстом гаджета). Собственно все.
Реализация гаджета
Сам по себе гаджет – это, по сути, zip-архив (правда, с измененным расширением .gadget) с манифестом внутри (файл gadget.xml) и файлами для работы гаджета (HTML-файлы, скрипты JavaScript, изображения для гаджета, CSS-файлы). Манифест содержит информацию о гаджете и о том, какой файл необходимо отобразить гаджетом.
<?xml version="1.0" encoding="utf-8" ?>
<gadget>
<name>DropBox заметки</name>
<namespace>microsoft.windows</namespace>
<version>1.0</version>
<author name="Барилко Виталий">
<info url="http://sys1c.ru" />
<logo src="Images/icon.png" />
</author>
<copyright>© 2012</copyright>
<description>Гаджет для отображения списка дел, которые синхронизируются через DropBox.</description>
<icons>
<icon src="Images/icon.png" />
</icons>
<hosts>
<host name="sidebar">
<base type="HTML" apiVersion="1.0.0" src="main.html" />
<permissions>Full</permissions>
<platform minPlatformVersion="1.0" />
</host>
</hosts>
</gadget>
Вот, что получаем в списке гаджетов:
Основные файлы работы гаджета
Для того, чтобы посмотреть на исходный код гаджета достаточно изменить расширение с ".gadget" на ".zip" и распаковать его любым архиватором.
Вот главный HTML-документ, который выполняет всю работу.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Unicode" />
<title>DropBox заметки</title>
<style type="text/css">
body
{
margin: 0;
}
#textBox
{
border: none;
position: absolute;
font-size: 9pt;
font-family: Segoe Print, Segoe Script, Segoe UI;
background: clear;
overflow: auto;
}
#erase_btn
{
position: absolute;
left: 2px;
bottom: 2px;
width: 16px;
height: 16px;
border: none;
z-index: 3;
border-width: 0;
}
</style>
<script type="text/javascript" src="main.js"></script>
</head>
<body unselectable="on" scroll="no" onload="initializeMain()">
<g:background id="backgroundObject" style="position:absolute;z-index:-1"/>
<textarea id="textBox" onkeyup="OnTextChanged()" style="left:0px;top:0px;padding:15px 15px 15px 15px;"></textarea>
<img id="rightGrippie" src="Images/grippie.png" style="position:absolute;right:6px; filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);"/>
<img id="bottomGrippie" src="Images/grippie.png" style="position:absolute;bottom:6px;"/>
<div onmousedown="resizeTimer(handleRight)" id="handleRight" style="width:16px;height:190px;position:absolute;right:0px;top:0px;cursor:e-resize;z-index:2;">
<img src="Images/spacer.gif" style="width:100%; height:100%"/>
</div>
<div onmousedown="resizeTimer(handleBottom)" id="handleBottom" style="position:absolute;height:16px;left:0px;bottom:0px;cursor:s-resize;margin:0pxz-index:2;">
<img src="Images/spacer.gif" style="width:100%; height:100%"/>
</div>
<div onmousedown="resizeTimer(handleCorner)" id="handleCorner" style="width:17px;height:17px;position:absolute;right:0px;bottom:0px;cursor:se-resize;z-index:2;">
<img src="Images/icon_resize.gif" style="width:16px; height:16px; z-index:3;"/>
</div>
<span onclick="eraseText()" tabindex="1"><img id="erase_btn" src="Images/erase_btn.png" title="Очистить" /></span>
</body>
</html>
Основной скрипт, в котором происходит загрузка и сохранение текстовой информации, проверка с интервалом на изменение файла и т.д.
var width, height; // Ширина и высота гаджета
var textFileName; // Имя файла с текстом гаджета
var fs; // Объект FileSystemObject
var timer, timerInterval; // Таймер и интервал таймера для проверки изменения текста в файле
var dateLastMod; // Дата последнего изменения файла
var startWidth, startHeight; // Изначальная ширина и высота гаджета
var basey, basex; // Системные
// Инициализация гаджета
function initializeMain() {
// Файл с настройками
System.Gadget.settingsUI = "settings.html";
System.Gadget.onSettingsClosed = updateSettings;
// Загружаем данные из файла в гаджет
LoadTextFile();
updateSettings();
updateDisplay();
// Подключаем таймер проверки изменения текста
timer = setInterval(checkText, updateinterval * 1000 * 60); // Переводим в минуты
}
// Загрузка данных из файла в гаджет
function LoadTextFile() {
// Читаем текст из файла (здесь проверка на существование файла)
fs = new ActiveXObject("Scripting.FileSystemObject");
var text = "";
textFileName = System.Gadget.Settings.read("textfilename");
if (textFileName){
if (fs.FileExists(textFileName)){
var f = fs.OpenTextFile(textFileName, 1, true, -1);
if (!f.AtEndOfStream)
text = f.ReadAll();
f.Close();
} else {
var f = fs.CreateTextFile(textFileName, true, true);
f.Close();
}
} else {
textFileName = System.Environment.getEnvironmentVariable("USERPROFILE") + "\DropBox\Tasks.txt";
if (fs.FileExists(textFileName)){
var f = fs.OpenTextFile(textFileName, 1, true, -1);
if (!f.AtEndOfStream)
text = f.ReadAll();
f.Close();
} else {
var f = fs.CreateTextFile(textFileName, true, true);
f.Close();
}
}
// Запоминаем время последнего изменения файла
dateLastMod = fs.GetFile(textFileName).dateLastModified;
// Загоняем текст в гаджет
if (text)
textBox.value = text;
else
textBox.value = "";
}
// Проверяем где дата файла старше и если в файле, то загружаем текст из файла
function checkText() {
var f = fs.GetFile(textFileName);
if (f.dateLastModified != dateLastMod){
// Дата изменилась загружаем
dateLastMod = f.dateLastModified;
var fi = fs.OpenTextFile(textFileName, 1, true, -1);
if (!f.AtEndOfStream)
textBox.value = fi.ReadAll();
fi.Close();
}
}
// Обновляем настройки
function updateSettings() {
var Color = System.Gadget.Settings.read("backgroundColor");
if (Color)
textBox.style.backgroundColor = Color;
else
textBox.style.backgroundColor = "FFFFB9";
var fontname = System.Gadget.Settings.read("fontname");
if (fontname)
textBox.style.fontFamily = fontname;
else
textBox.style.fontFamily = "Segoe UI";
var fontsize = System.Gadget.Settings.read("fontsize");
if (fontsize)
textBox.style.fontSize = fontsize + "pt";
else {
fontsize = "10"
textBox.style.fontSize = "10pt";
}
var tempWidth = System.Gadget.Settings.read("width");
if (tempWidth)
width = tempWidth;
else
width = "200";
var tempHeight = System.Gadget.Settings.read("height");
if (tempHeight)
height = tempHeight;
else
height = "200";
var tempupdateinterval = System.Gadget.Settings.read("updateinterval");
if (tempupdateinterval)
updateinterval = tempupdateinterval;
else
updateinterval = 1;
var tempFileName = System.Gadget.Settings.read("textfilename");
if (tempFileName)
textFileName = tempFileName;
else
LoadTextFile();
// Меняем цвет фона и скрола
document.body.style.backgroundColor = textBox.style.backgroundColor;
document.body.style.scrollbarFaceColor = textBox.style.backgroundColor;
document.body.style.scrollbarFaceColor = textBox.style.backgroundColor;
document.body.style.scrollbarTrackColor = textBox.style.backgroundColor;
document.body.style.scrollbarShadowColor = textBox.style.backgroundColor;
document.body.style.scrollbarHighlightColor = textBox.style.backgroundColor;
document.body.style.scrollbar3dlightColor = textBox.style.backgroundColor;
document.body.style.scrollbarDarkshadowColor = textBox.style.backgroundColor;
// Сохраняем настройки
System.Gadget.Settings.write("backgroundColor", textBox.style.backgroundColor);
System.Gadget.Settings.write("fontname", textBox.style.fontFamily);
System.Gadget.Settings.write("fontsize", fontsize);
System.Gadget.Settings.write("width", width);
System.Gadget.Settings.write("height", height);
System.Gadget.Settings.write("textfilename", textFileName);
System.Gadget.Settings.write("updateinterval", updateinterval);
}
// При изменении текста в гаджете
function OnTextChanged() {
var f = fs.CreateTextFile(textFileName, true, true);
f.Write(textBox.value);
f.Close();
}
// Очистка текста
function eraseText() {
textBox.value = "";
OnTextChanged();
}
// Обновление
function updateDisplay() {
document.body.style.height = height;
document.body.style.width = width;
textBox.style.width = width - 16;
handleBottom.style.width = width - 18;
textBox.style.height = height - 16;
handleRight.style.height = height - 18;
rightGrippie.style.top = Math.floor(height / 2 - 13);
bottomGrippie.style.left = Math.floor(width / 2 - 13);
}
// Растягивание в гаджете
function resizeTimer(field) {
field.setCapture();
startWidth = parseInt(document.body.style.width);
startHeight = parseInt(document.body.style.height);
basey = event.y;
basex = event.x;
field.onmousemove = function () {
doResize(field);
}
field.onmouseup = function () {
field.releaseCapture();
field.onmousemove = null;
field.onmouseup = null;
doResize(field);
System.Gadget.Settings.write("width", width);
System.Gadget.Settings.write("height", height);
}
}
function doResize(field) {
if (field == handleRight || field == handleCorner) {
width = startWidth + event.x - basex;
width = width < 40 ? 40 : width;
}
if (field == handleBottom || field == handleCorner) {
height = startHeight + event.y - basey;
height = height < 40 ? 40 : height;
}
updateDisplay();
}
Как установить гаджет
Внимание! Для работы гаджета необходим установленный DropBox. Причем путь к синхронизируемой папке должен быть такой, какой DropBox предлагает при установке! Если вдруг это не так, то ничего страшного, можно установить гаджет, открыть настройки и вручную указать путь к файлу, в котором будет храниться текст нашей заметки.
Скачайте гаджет (в конце статьи) и запустите его:
Нажимаем «Установить». Готово, гаджет установлен, а на рабочем столе появилась заметка, которой уже можно пользоваться.
Минусы
Все таки, без минусов никуда. Есть проблемы, которые на данный момент не получилось устранить.
- Если папка DropBox пенесена в другое место, отличное от того, куда она устанавливается по умолчанию, то гаджет сразу не заработает. Необходимо будет открыть настройки гаджета и указать путь к файлу.
- Нет форматирования в заметке. Только обычный текст, без курсива, жирного, зачеркнутого текста и т.п. Была попытка подвязать к гаджету WYSIWYG-редакторы (TinyMCE, CKEditor), но заставить их нормально работать в гаджете из коробки не получилось.
Подведем итоги
Лично для себя я получил именно то, что хотел: простой и удобный гаджет, который со мной и дома и на работе.
Так же, хочу отдельно сказать, что для людей, которые хорошо знают HTML + CSS + JavaScript сделать хороший гаджет, который облегчит жизнь очень просто.
Надеюсь, моя статья оказалась полезной для Вас.
Автор: Diversus