Sidebar Gadget Sticky Notes с синхронизацией через DropBox для Windows 7 своими руками

    Очень долго я искал для себя маленькую записную книжку для хранения важной информации на компьютере, которая была бы на моем рабочем столе на работе и дома. В Windows 7 есть даже специальная программка, которая называется Sticky Notes, она всем хороша, но имеет три существенных (для меня) минуса:
    1. Она не имеет встроенной синхронизации с несколькими компьютерами (точнее сделать то ее можно, через тот же DropBox, но с бубном);
    2. Она постоянно висит в панели задач в списке открытых окон;
    3. При нажатии на Ctrl+D (свернуть все окна), она сворачивается, как и любое другое окно. Что не очень удобно.


    Также в сети была найдена программа Evernote Sticky Notes. Она также имеет из недостатков два последних пункта, но позволяет через учетную запись Evernote производить синхронизацию. После не продолжительного использования, я от нее решил отказаться.
    Итак, что же делать?

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

    Основные возможности:
    1. Заметки сделаны в виде гаджета для боковой панели Windows 7;
    2. Синхронизация хранимых данных через DropBox между несколькими компьютерами;
    3. Возможность изменения цвета заметки, шрифта, размера шрифта;
    4. Изменение размеров заметки с помощью мышки;

    Реализация гаджета


    Я решил сделать гаджет для Windows, который полностью бы удовлетворял меня во всех отношениях.
    Механизм гаджетов в Windows позволяет не мешаться в панели задач, на них не распространяется сворачивание всех окон, они все равно будут занимать гордое место на рабочем столе. А если хранить файл с текстом заметки в папке DropBox, то можно добиться синхронизации записок на нескольких компьютерах.
    Дабы избежать вопросов почему выбран именно DropBox, отвечу сразу: он мне нравится, и я его постоянно использую, а также определить папку, куда DropBox устанавливается по умолчанию достаточно просто: эта папка обычно находится здесь (на JavaScript):

    System.Environment.getEnvironmentVariable("USERPROFILE") + "\\DropBox\\"

    Алгоритм работы гаджета


    Алгоритм работы гаджета очень простой.
    Гаджет хранит путь к файлу, в котором содержится текст с отображаемой информацией. Этот файл находится в DropBox. При изменении текста в гаджете, измененная информация записывается в файл. DropBox после изменения файла сам синхронизирует данные между компьютерами, это уже головная боль DropBox.
    Время от времени с помощью JavaScript, происходит проверка, не изменилась ли дата последнего изменения файла с информацией, которую отображает гаджет и реальным файлом. Если дата изменилась, то загружается обновленный текст (это значит, что текст гаджета был изменен на другом компьютере, либо после включения компьютера DropBox не успел обновить файл с текстом гаджета). Собственно все.

    Реализация гаджета


    Сам по себе гаджет – это, по сути, zip-архив (правда, с измененным расширением .gadget) с манифестом внутри (файл gadget.xml) и файлами для работы гаджета (HTML-файлы, скрипты JavaScript, изображения для гаджета, CSS-файлы). Манифест содержит информацию о гаджете и о том, какой файл необходимо отобразить гаджетом.

    gadget.xml
    <?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-документ, который выполняет всю работу.
    main.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>

    Основной скрипт, в котором происходит загрузка и сохранение текстовой информации, проверка с интервалом на изменение файла и т.д.
    main.js
    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;
    
      fs = new ActiveXObject("Scripting.FileSystemObject");  
    
      // Загружаем данные из файла в гаджет
      LoadTextFile();
    
      updateSettings();
      updateDisplay();
    
      // Подключаем таймер проверки изменения текста
      timer =  setInterval(checkText, updateinterval * 1000 * 60); // Переводим в минуты
    }
    
    // Раскодировка строки в Base64
    function base64_decode (data) {
      var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
      var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
      ac = 0,
      dec = "",
      tmp_arr = [];
      if (!data) {
        return data;
      }
      data += '';
      do { // unpack four hexets into three octets using index points in b64
        h1 = b64.indexOf(data.charAt(i++));
        h2 = b64.indexOf(data.charAt(i++));
        h3 = b64.indexOf(data.charAt(i++));
        h4 = b64.indexOf(data.charAt(i++));
        bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
        o1 = bits >> 16 & 0xff;
        o2 = bits >> 8 & 0xff;
        o3 = bits & 0xff;
    
        if (h3 == 64) {
          tmp_arr[ac++] = String.fromCharCode(o1);
        } else if (h4 == 64) {
          tmp_arr[ac++] = String.fromCharCode(o1, o2);
        } else {
          tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
        }
      } while (i < data.length);
      dec = tmp_arr.join('');
      return dec;
    }
    
    // Загрузка данных из файла в гаджет
    function LoadTextFile() {
      // Читаем текст из файла (здесь проверка на существование файла)  
      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 {
        try {
          // Определяем путь из файла host.db настроек DropBox
          var dbPath = System.Environment.getEnvironmentVariable("APPDATA");
          var fconfig = fs.OpenTextFile(dbPath + "\\Dropbox\\host.db", 1, false, 0);
          var s = fconfig.ReadLine();
          var folderPath = base64_decode(fconfig.ReadLine());
        } catch(e) {
          // Если по какой то причине не сработало, то присваиваем путь по профайлу пользователя
          var folderPath = System.Environment.getEnvironmentVariable("USERPROFILE") + "\\Dropbox";
        } 
        
        textFileName = folderPath + "\\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;
      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 предлагает при установке! Если вдруг это не так, то ничего страшного, можно установить гаджет, открыть настройки и вручную указать путь к файлу, в котором будет храниться текст нашей заметки.
    Скачайте гаджет (в конце статьи) и запустите его:

    Нажимаем «Установить». Готово, гаджет установлен, а на рабочем столе появилась заметка, которой уже можно пользоваться.

    Минусы


    Все таки, без минусов никуда. Есть проблемы, которые на данный момент не получилось устранить.
    1. Если папка DropBox пенесена в другое место, отличное от того, куда она устанавливается по умолчанию, то гаджет сразу не заработает. Необходимо будет открыть настройки гаджета и указать путь к файлу. Этот минус устранен.
    2. Нет форматирования в заметке. Только обычный текст, без курсива, жирного, зачеркнутого текста и т.п. Была попытка подвязать к гаджету WYSIWYG-редакторы (TinyMCE, CKEditor), но заставить их нормально работать в гаджете из коробки не получилось.

    Подведем итоги


    Лично для себя я получил именно то, что хотел: простой и удобный гаджет, который со мной и дома и на работе.
    Так же, хочу отдельно сказать, что для людей, которые хорошо знают HTML + CSS + JavaScript сделать хороший гаджет, который облегчит жизнь очень просто.

    Скачать гаджет

    Надеюсь, моя статья оказалась полезной для Вас.

    PS: Исправил найденные ошибки.
    Теперь папка синхронизации DropBox определяется программно с помощью JavaScript (отдельное спасибо пользователю isden за ссылку). Так же при изменении имени файла в настройках изменения принимаются сразу.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 28
    • 0
      Feedback:

      Установил, появился маленький желтенький квадрат и все. Гаджет как бы есть, но его и нет.
      • 0
        У Вас папка синхронизации DropBox установлена не в папку USERPROFILE\DropBox (см. Минусы п.1).
        Для исправления этой проблемы измените в настройках путь к файлу с заметками.
        • 0
          Путь бы изменен. Но ни к чему это не привело. Сношу все подозрения на кривые руки себя.
          • 0
            Сделав какие то манипуляции с гаджетов, кроме указания имени, получилось только вот такое. cl.ly/JaE3
            • 0
              Да, по всей видимости, есть ошибка с сохранением файла для сохранения.
              Надо будет переделать определение пути и проблема будет решена.
              • 0
                Жду апдейта!)
                • +1
                  Столкнулся с идентичной проблемой, переименовал в .zip, подправил пути на мой DropBox в .js в файлах, упаковал обратно, и все заработало, можно пользоваться и ждать апдейта

                  Спасибо автору за любовь к DropBox и интересный виджет.
      • 0
        Гаджет заинтересовал, жаль не нашел пока офф способа вклюить гаджеты на виндовс 8 :( Но протестирую как только смогу. Только вот вопрос такой… касательно этого места:
        … а также определить папку, куда DropBox устанавливается по умолчанию достаточно просто: эта папка обычно находится здесь (на JavaScript):

        System.Environment.getEnvironmentVariable(«USERPROFILE») + "\\DropBox\\"

        Несколько опрометчивое заявление (или я один такой?) забивать место на системном разделе да еще и в профиле пользователя для меня несколько странно так что дропбокс, с 20+гб его содержимого живёт в корне диска D:
        • +1
          Путь к файлу можно поменять в настройках
          • +2
            Можно брать путь до каталога из настроек Dropbox. Посмотрите как сделано например вот здесь — stackoverflow.com/questions/9660280/how-do-i-programmatically-locate-my-dropbox-folder-using-c
            • 0
              Вот это позволит убрать один минус! Исправлю, обязательно. Спасибо.
              • 0
                Возможно contenteditable="true" частично решит второй минус

                Решил проверить
                <!-- textarea id="textBox" onkeyup="OnTextChanged()" style="left:0px;top:0px;padding:15px 15px 15px 15px;"></textarea -->
                <div id="textBox" contentEditable="true" onkeyup="OnTextChanged()" style="left:0px;top:0px;padding:15px 15px 15px 15px;"></div>
                

                Заменил textarea на <div contentEditable="true">
                Результат
                1. Теперь можно выделить жирным и курсивом с клавиатуры.
                2. Не работает выделение текста мишкой, передвигается весь виджет.

                • 0
                  У меня было так же. Т.е. редактор видно, но текст в нем нормально набирать нельзя.
          • 0
            В Windows 8 гаджеты убраны. Скорее всего кто-то сделает порт из семерки, но нативно их там вообще нет.
        • 0
          А для WYSIWYG попробуйте что-то такое — nicedit.com/ — оно достаточно небольшое и простое, возможно заработает.
          • 0
            А для WYSIWYG попробуйте что-то такое — nicedit.com/ — оно достаточно небольшое и простое, возможно заработает.


            Попробовал, не работает как надо… Все таки, видимо, ограничения в гаджетах не позволяют использовать JavaScript на полную мощь.
          • 0
            Гаджеты из Windows 8 выпилили, так что в скором времени придется портировать. Было бы нативным — не задумываясь скачал бы. А так, увы…
            • 0
              Надо заметить, что для Microsoft настоятельно советует не пользоваться гаджетами в Windows Vista и 7. Ссылка на новость с громким заголовком и оригинал сообщения от MS.
              • 0
                Интересная штука.
                Но тут всё же синхронизация с dropbox организована просто как работа с банальной директорией на диске, ничего конкретно «дропбоксовского». Посмотрите на javascript dropbox api клиента github.com/dropbox/dropbox-js и tech.dropbox.com/?p=345 может подойдёт? (у меня есть сомнения в работоспособности этой библиотеке для гаджетов — но попытка не пытка)
                • 0
                  Да — я что то не понял — почему гаджет называется дропбокс заметки.

                  Я вот пользуюсь например shugar sync (он намного круче дропбокса как синхронизатор, умеет работать с разными папками и т.д.). Насколько я понимаю данный продукт не имеет никакого отношения к дропбоксу и я смогу им пользоваться, синхронизируя заметки через свой синхронизатор?
                  • 0
                    Называется «DropBox заметки» потому, что автоматически при установке гаджета определяется папка синхронизации DropBox.
                    Хотя, в принципе, гаджет можно использовать для любого подобного сервиса (shugar sync, яндекс диск и т.д.) изменив местоположение файла синхронизации.
                  • 0
                    А что мешает создать обычный текстовый файл, поместить его в папку любого облачного сервиса синхронизации и сделать для него ярлык на рабочем столе? Или статья о том, как сделать виджет для windows? А да, точно — цвет фона, шрифт и его размер, это очень важно, наверное, для записей, вроде «Надо прочитать книжку «Куда уходит время? Советы по эффективности»». Тогда, спасибо.
                    P.S. Лично я пользуюсь «Scratchpad» for Chrome, синхронизируется с гуглодоками.
                    • 0
                      А что мешает создать обычный текстовый файл, поместить его в папку любого облачного сервиса синхронизации и сделать для него ярлык на рабочем столе?

                      Ничего не мешает :)

                      Можно придумать еще вопросы:
                      1) Зачем гаджет «Часы»? Можно ведь в области уведомлений на панели задач посмотреть время.
                      2) Зачем гаджет для отображения даты? Я текущую дату и так знаю…
                      3) Зачем гаджет «Погода»? Можно ведь сделать закладку на Gismeteo в браузере и все…
                      и т.д.

                      Все дело в удобстве. Мне так удобно, я использую гаджеты.
                      Если мне нужен гаджет, который позволит хранить какие то важные записки и дома и на работе, то почему нет?
                      Конечно можно использовать и ярлык на текстовый файл, но гаджет будет постоянно перед глазами…

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.