[Tkabber-dev] r492 - in trunk/plugins/notes: . doc ifaces

tkabber-svn at jabber.ru tkabber-svn at jabber.ru
Wed Aug 3 15:55:53 MSD 2011


Author: Totktonada
Date: 2011-08-03 15:55:52 +0400 (Wed, 03 Aug 2011)
New Revision: 492

Added:
   trunk/plugins/notes/doc/
   trunk/plugins/notes/doc/LINKS
   trunk/plugins/notes/doc/TODO
   trunk/plugins/notes/doc/proto
   trunk/plugins/notes/ie.tcl
   trunk/plugins/notes/ifaces/
   trunk/plugins/notes/ifaces/gui.tcl
Removed:
   trunk/plugins/notes/LINKS
   trunk/plugins/notes/TODO
   trunk/plugins/notes/proto
Modified:
   trunk/plugins/notes/notes.tcl
Log:
Notes plugin:

1. Split GUI and base of plugin to proper files.
2. Add simple tags filter.
3. Add initial implementation export to file.
4. Small fixes.


Deleted: trunk/plugins/notes/LINKS
===================================================================
--- trunk/plugins/notes/LINKS	2011-07-13 19:08:21 UTC (rev 491)
+++ trunk/plugins/notes/LINKS	2011-08-03 11:55:52 UTC (rev 492)
@@ -1,4 +0,0 @@
-"XEP-0049: Private XML Storage" - http://xmpp.org/extensions/xep-0049.html
-Source of Miranda "Jabber notes" plugin - http://code.google.com/p/miranda/source/browse/trunk/miranda/protocols/JabberG/jabber_notes.cpp
-Description for Psi+ "Storage Notes" plugin - http://psi-plus.com/wiki/doku.php/plugins#storage_notes_plugin
-Source of Psi+ "Storage Notes" plugin - http://psi-dev.googlecode.com/svn/trunk/plugins/generic/storagenotesplugin/

Deleted: trunk/plugins/notes/TODO
===================================================================
--- trunk/plugins/notes/TODO	2011-07-13 19:08:21 UTC (rev 491)
+++ trunk/plugins/notes/TODO	2011-08-03 11:55:52 UTC (rev 492)
@@ -1,33 +0,0 @@
-Примерные наброски идей на будущее.
-
-"/notes list"
-"/notes add" - появляется окошко с полями ввода
-"/notes del #n"
-"/notes edit #n" - окошко с полями ввода
-
-Длинные команды:
-
-/notes add
-tag1 tag2 tag3
-title
-text
-
-/notes edit #n
-tag tag tag
-title
-text
-
-Команда "/todo текст текст текст" - добавляет новую заметку с тегом "todo".
-Заголовок - ?
-
-=======================
-
-Можно вообще таб в Juick-style сделать. Пишешь туда — добавляешь заметку. E #номер — редактирование. D #номер — удалить. #номер *tag — добавить/удалить тег. # — вывести все заметки.
-
-======================
-
-Быстрая запись в заметку жуйкосущностей, ссылок и прочего. Возможность прикрепить заметку к JID'у, смотреть из меню соотв. контакта. Сохранять время последней модицикации. Выделение -> ПКМ -> заметки.
-
-Обработка разнообразных ошибок при чтении из хранилища или записи в него.
-
-[01:54:36]<Totktonada> ancestor: Оно уже есть. Другая реализация. Да, я хотел прикрутить их к JID'ам, но как бы опционально и с отображением в едином списке. Теперь надо думать ещё и над тем, делать ли совместимо с теми заметками или лучше просто добавить возможность импорта оттуда.

Added: trunk/plugins/notes/doc/LINKS
===================================================================
--- trunk/plugins/notes/doc/LINKS	                        (rev 0)
+++ trunk/plugins/notes/doc/LINKS	2011-08-03 11:55:52 UTC (rev 492)
@@ -0,0 +1,4 @@
+"XEP-0049: Private XML Storage" - http://xmpp.org/extensions/xep-0049.html
+Source of Miranda "Jabber notes" plugin - http://code.google.com/p/miranda/source/browse/trunk/miranda/protocols/JabberG/jabber_notes.cpp
+Description for Psi+ "Storage Notes" plugin - http://psi-plus.com/wiki/doku.php/plugins#storage_notes_plugin
+Source of Psi+ "Storage Notes" plugin - http://psi-dev.googlecode.com/svn/trunk/plugins/generic/storagenotesplugin/

Added: trunk/plugins/notes/doc/TODO
===================================================================
--- trunk/plugins/notes/doc/TODO	                        (rev 0)
+++ trunk/plugins/notes/doc/TODO	2011-08-03 11:55:52 UTC (rev 492)
@@ -0,0 +1,141 @@
+Примерные наброски идей на будущее.
+
+"/notes list"
+"/notes add" - появляется окошко с полями ввода
+"/notes del #n"
+"/notes edit #n" - окошко с полями ввода
+
+Длинные команды:
+
+/notes add
+tag1 tag2 tag3
+title
+text
+
+/notes edit #n
+tag tag tag
+title
+text
+
+Команда "/todo текст текст текст"
+Добавляет новую заметку с тегом "todo".
+Заголовок - ?
+
+=======================
+
+Можно вообще таб в Juick-style сделать. Пишешь туда — добавляешь заметку. E
+#номер — редактирование. D #номер — удалить. #номер *tag — добавить/удалить
+тег. # — вывести все заметки.
+
+======================
+
+Быстрая запись в заметку жуйкосущностей, ссылок и прочего. Возможность
+прикрепить заметку к JID'у, смотреть из меню соотв. контакта. Сохранять время
+последней модицикации. Выделение -> ПКМ -> заметки.
+
+Обработка разнообразных ошибок при чтении из хранилища или записи в него.
+
+[01:54:36]<Totktonada> ancestor: Оно уже есть. Другая реализация. Да, я хотел
+прикрутить их к JID'ам, но как бы опционально и с отображением в едином списке.
+Теперь надо думать ещё и над тем, делать ли совместимо с теми заметками или
+лучше просто добавить возможность импорта оттуда.
+
+======================
+
+При экспорте в файл (файлы?) фиксировать изменения с помощью vcs. Иначе говоря,
+интеграция с одной/несколькими vcs. Хорошо продумать идею, use-cases.
+
+======================
+
+Продумать загрузку/выгрузку gui.tcl.
+
+Подумать над тем, куда подцеплять импорт/экспорт в файл. Можно подключать к
+экспорту работу с vcs "плагинами" (из разных файлов).  Как вариант
+импорта/экспорта: абстракция «хранилищ», где хранилищем является как private
+storage, так и файл на диске. Можно оформить хранилище-файл как библиотеку
+(аналогично с ./xmpp/xmpp-notes.tcl)
+
+Отдельная от хранилищ фишка: экспорт в определённый файл по тегу (тегам). Можно
+в gui.tcl добавить кнопку «экспортнуть текущее», чтобы сдампить содержимое
+lbox.
+
+======================
+
+Может объединить update_lbox и update_lbox_at с помощью
+proc ляляля {{idx all} {new_note {}}} {...} ?)
+
+======================
+
+xmpp -> libs (или lib). Туда реализацию файлового хранилища добавить.
+
+======================
+
+Баг: $idx при записи отредактированной заметки не совпадает с индексом в
+$notes($current_xlib). Лучший вариант исправления - ввести в формат заметки её
+индекс.
+
+======================
+
+Пустой заголовок → Тело отображается в его качестве.
+
+[Done] Возможно, стоит отображать теги. Вообще, стоит подумать над фоматом
+отображения заметок в списке.
+
+======================
+
+Запрос на подтверждение при удалении заметки, надо ли?
+
+======================
+
+Теги: выпадающий список в панели вверху. {Все, тег1, тег2, тег3, несколько
+тегов}. Послений пункт — появляется окошко с галочками.
+
+[23:37:43]<Totktonada> diSabler: Теги, кстати, уже придумал как сделать. Выбор
+обычным выпадающим меню, в котором есть два особых пункта: «All» и «Choose». По
+второму выскакивает окошко, в котором галочками отмечаешь, сообщения с какими
+именно тегами отображать
+
+[23:39:20]<diSabler> Totktonada: ещё текстовое поле надо. при большом
+количестве тегов - проще искать, а не выбирать.
+
+[23:42:36]<Totktonada> diSabler: Тогда можно сделать как в gmrun примерно. Т.е.
+ввод обычный, но по <Tab> набираемое слово дополняется до ближайшего
+подходязего тега. Потом — до следующего.
+
+[23:42:56]<Totktonada> Так даже интересней получается.
+
+[23:43:26]<diSabler> да
+
+[23:44:05]<diSabler> но при этом не хочится терять возможность просмотра тегов
+
+[23:45:44]<Totktonada> diSabler: Потабать — не вариант? А если (опять же, как в
+gmrun) по Tab'у будет не только подставляться тег, но и появляться выпадающий
+список с подходящими тегами?
+
+[23:46:31]<diSabler> Totktonada: хз. надо продумать варианты.
+
+======================
+
+Экспорт/импорт. Опция для сохранения заметок в файл, а не в private storage.
+
+======================
+
+Сохранять все элементы внутри хранилища, чтобы при сохранени они оставались. Подумать над тем, хранить ли информацию о дате создания/модификации и прочую, не поддерживаемую другими клиентами в том же хранилище (с риском, что её затрёт другой клиент) или в другом.
+
+======================
+
+[23:44:13]<Totktonada> Хм, что-то когда открыта вкладка с заметками Tkabber
+чуть подтормаживает. До последнего коммита не замечал. Странно.
+
+UPD: Видимо, из-за чего-то другого, сейчас всё нормально.
+
+======================
+
+Место для полосы прокрутки в окошке редактирования остаётся даже когда полоса
+прокрутки не нужна.
+
+======================
+
+Изменения из строки для ввода тегов по Enter'у надо добавлять в промежуточную
+переменную, чтобы при экспорте и прочей обработке обрабатывалось именно то, что
+в lbox.

Added: trunk/plugins/notes/doc/proto
===================================================================
--- trunk/plugins/notes/doc/proto	                        (rev 0)
+++ trunk/plugins/notes/doc/proto	2011-08-03 11:55:52 UTC (rev 492)
@@ -0,0 +1,63 @@
+See XEP-0049.
+
+===================
+Request to receive notes:
+===================
+
+<iq type='get' id='id1'>
+    <query xmlns='jabber:iq:private'>
+        <storage xmlns='http://miranda-im.org/storage#notes'/>
+    </query>
+</iq>
+
+===================
+Positive response:
+===================
+
+<iq type='result' to='myjid at server.tld' id='id1'>
+    <query xmlns='jabber:iq:private'>
+        <storage xmlns='http://miranda-im.org/storage#notes'>
+
+            <note tags='tag1 tag2 tag3'>
+                <title>Title 1</title>
+                <text>Note body 1</text>
+            </note>
+
+            <note tags=''>
+                <title>Title 2</title>
+                <text>Note body 2</text>
+            </note>
+
+        </storage>
+    </query>
+</iq>
+
+===================
+Send notes to server:
+===================
+
+<iq type='set' id='id1'>
+    <query xmlns='jabber:iq:private'>
+        <storage xmlns='http://miranda-im.org/storage#notes'>
+	
+            <note tags='tag1 tag2 tag3'>
+                <title>Title 1</title>
+                <text>Note body</text>
+            </note>
+
+            <note tags=''>
+                <title>Title 2</title>
+                <text>Note body 2</text>
+            </note>
+
+        </storage>
+    </query>
+</iq>
+
+===================
+Positive response:
+===================
+
+<iq type='result' to='myjid at server.tld' id='id1'>
+    <query xmlns='jabber:iq:private'/>
+</iq>

Added: trunk/plugins/notes/ie.tcl
===================================================================
--- trunk/plugins/notes/ie.tcl	                        (rev 0)
+++ trunk/plugins/notes/ie.tcl	2011-08-03 11:55:52 UTC (rev 492)
@@ -0,0 +1,37 @@
+namespace eval ie {
+# TODO:
+if {0} {
+    variable version 1.0
+
+    if {![file exists $options(notes_dir)]} {
+        file mkdir $options(notes_dir)
+
+        # Storing version for possible future conversions
+        set fd [open [file join $options(notes_dir) version] w]
+        puts $fd $version
+        close $fd
+    }
+}
+}
+
+####################################################################
+# Mapping symbols from ${PATH_TO_TKABBER}/plugins/chat/logger.tcl
+
+proc ie::str_to_log {str} {
+    return [string map {\\ \\\\ \r \\r \n \\n} $str]
+}
+
+proc ie::log_to_str {str} {
+    return [string map {\\\\ \\ \\r \r \\n \n} $str]
+}
+
+####################################################################
+# Export
+
+proc ie::export {notes_file {search_tags {}}} {
+    set fd [open $notes_file w]
+    foreach note [::plugins::notes::get_notes $search_tags] {
+        puts $fd [str_to_log $note]
+    }
+    close $fd
+}

Added: trunk/plugins/notes/ifaces/gui.tcl
===================================================================
--- trunk/plugins/notes/ifaces/gui.tcl	                        (rev 0)
+++ trunk/plugins/notes/ifaces/gui.tcl	2011-08-03 11:55:52 UTC (rev 492)
@@ -0,0 +1,383 @@
+namespace eval ifaces::gui {
+# TODO: need here, or only in ::plugins::notes?
+#    package require xmpp::private::notes
+#    package require msgcat
+}
+
+# TODO: variable lbox in all proc, where $lbox used, changed this variable in create_nptes_tab. Also: conn_menu.
+
+proc ifaces::gui::load {} {
+    hook::add plugins_notes_changed_connections_hook \
+        [namespace current]::changed_connections
+    hook::add plugins_notes_changed_current_connection_hook \
+        [namespace current]::changed_current_connection
+    hook::add plugins_notes_changed_note_hook \
+        [namespace current]::changed_note
+
+    hook::add finload_hook [namespace current]::setup_menu
+
+    setup_menu
+}
+
+proc ifaces::gui::unload {} {
+    desetup_menu
+    destroy_win .notes
+#TODO: destroy edit_note window
+# and export_notes window
+
+    hook::remove finload_hook [namespace current]::setup_menu
+
+    hook::remove plugins_notes_changed_connections_hook \
+        [namespace current]::changed_connections
+    hook::remove plugins_notes_changed_current_connection_hook \
+        [namespace current]::changed_current_connection
+    hook::remove plugins_notes_changed_note_hook \
+        [namespace current]::changed_note
+
+}
+
+proc ifaces::gui::changed_connections {} {
+    if {![winfo exists .notes]} return
+
+    update_connections_menu
+}
+
+proc ifaces::gui::changed_current_connection {} {
+    if {![winfo exists .notes]} return
+
+# TODO: drop all editboxes
+    update_connections_menu_label
+    update_lbox
+}
+
+proc ifaces::gui::changed_note {idx new_note} {
+    if {![winfo exists .notes]} return
+
+    update_lbox_at $idx $new_note
+}
+
+####################################################################
+# GUI
+
+proc ifaces::gui::setup_menu {} {
+    catch {
+        set m [.mainframe getmenu plugins]
+
+        $m add command -label [::msgcat::mc "Notes"] \
+            -command [list [namespace current]::open_window]
+    }
+}
+
+proc ifaces::gui::desetup_menu {} {
+    catch {
+        set m [.mainframe getmenu plugins]
+        set ind [$m index [::msgcat::mc "Notes"]]
+
+        $m delete $ind
+    }
+}
+
+proc ifaces::gui::open_window {} {
+    set w .notes
+
+    if {[winfo exists $w]} {
+        raise_win $w
+        return
+    }
+
+#    if {[llength [::plugins::notes::connections]] == 0} return
+
+    add_win $w -title [::msgcat::mc "Notes"] \
+        -tabtitle [::msgcat::mc "Notes"] \
+        -class Notes \
+        -raise 1
+
+    create_notes_tab $w
+
+# For storing notes after close tab uncomment next line.
+#    bind $w <Destroy> +[list [namespace current]::store_all_notes]
+# Now: store notes immediately after edit.
+}
+
+proc ifaces::gui::create_notes_tab {w} {
+# ==== Button box ====
+    set tools [frame $w.tools -borderwidth 5]
+    pack $tools -side top -fill y -anchor w
+
+# ==== Buttons ====
+    set new_button [button $tools.new_button -text [::msgcat::mc "New note"] \
+        -command [list [namespace current]::edit_note end]]
+    pack $new_button -anchor w -side left
+    set delete_button [button $tools.delete_button -text [::msgcat::mc "Delete note"] \
+        -command [list [namespace current]::delete_focused_note]]
+    pack $delete_button -anchor w -side left
+
+# ==== List of connections ====
+    set conn_button [menubutton $tools.conn_button \
+        -menu $tools.conn_button.menu]
+# TODO: keyboard bindings for "arrow up/down" keys, etc.
+#        -takefocus 1]
+    set conn_menu [menu $conn_button.menu -tearoff 0]
+    update_connections_menu
+    update_connections_menu_label
+    pack $conn_button -anchor w -side left
+# TODO: from menubutton to OptionMenu?
+#    set conn_menu [OptionMenu $tools.conn_menu [::plugins::notes::connections]]
+
+# ==== Tags ====
+    set tags [entry $tools.tags]
+    pack $tags -anchor w -side left
+
+# ==== Export ====
+    set export_button [button $tools.export_button -text [::msgcat::mc "Export"] \
+        -command [list [namespace current]::export_notes_window]]
+    pack $export_button -anchor w -side left
+
+# ==== lbox frame ====
+    set lbox_frame [frame $w.lbox_frame]
+    pack $lbox_frame -side left -fill both -expand true -anchor w
+
+# ==== lbox frame scrolled widget ====
+    grid columnconfigure $lbox_frame 0 -weight 1
+    set sw [ScrolledWindow $lbox_frame.sw]
+
+    set lbox [listbox $lbox_frame.lbox -takefocus 1 -exportselection 0]
+    update_lbox
+    focus $lbox
+# TODO: move focus $lbox to update_lbox/update_lbox_at?
+
+# ==== Bind ====
+    # From ${PATH_TO_TKABBER}/plugins/chat/histool.tcl
+    # Workaround for a bug in listbox (can't get focus on mouse clicks):
+    bind Listbox <Button-1> {+ if {[winfo exists %W]} {focus %W}}
+
+    bind $lbox <Double-Button-1> [namespace code {
+        edit_note [%W nearest %y]
+    }]
+
+    bind $lbox <Return> [namespace code {
+        edit_note [%W index active]
+    }]
+
+    bind $tags <Return> [namespace code {
+        update_lbox
+    }]
+
+# ==== Other ====
+    $sw setwidget $lbox
+    grid $sw -sticky news
+    grid rowconfigure $lbox_frame 0 -weight 1
+}
+
+proc ifaces::gui::update_connections_menu_label {} {
+    if {![winfo exists .notes]} return
+
+    set conn_button .notes.tools.conn_button
+    $conn_button configure -text [::plugins::notes::get_current_connection_name]
+}
+
+proc ifaces::gui::update_connections_menu {} {
+    puts "called update_connections_menu"
+    set conn_menu .notes.tools.conn_button.menu
+    $conn_menu delete 0 end
+    puts "update_connections_menu: deleted items"
+
+    foreach conn [::plugins::notes::connections] {
+#TODO: replace connection_jid with proc similar to ::plugins::notes::get_current_connection_name
+        $conn_menu add command \
+            -label [connection_jid $conn] \
+            -command [namespace code [format {
+                ::plugins::notes::set_current_xlib %1$s
+            } $conn]]
+    }
+}
+
+proc ifaces::gui::delete_focused_note {} {
+#TODO: ask "Are you sure..?".
+    if {[llength [plugins::notes::connections]] == 0} return
+
+    set lbox .notes.lbox_frame.lbox
+    set idx [$lbox index active]
+    ::plugins::notes::set_note [::plugins::notes::get_real_index $idx [get_filter_tags]] $idx {}
+}
+
+proc ifaces::gui::edit_note {idx args} {
+    if {[llength [plugins::notes::connections]] == 0} return
+
+    if {[cequal $idx end]} {
+        set title ""
+        set tags_str ""
+        set text ""
+    } else {
+        set source_note [::plugins::notes::get_note $idx [get_filter_tags]]
+        ::xmpp::private::notes::split $source_note title -> text tags_str
+    }
+
+    set dialog_w .edit_note
+    catch { destroy $dialog_w }
+    set dialog_w [Dialog $dialog_w -title [::msgcat::mc "Edit note"] \
+        -separator 1 \
+        -anchor e \
+        -default 0 \
+        -cancel 1 \
+        -modal none]
+
+    set dialog_frame [$dialog_w getframe]
+    grid columnconfigure $dialog_frame 1 -weight 1
+
+    label $dialog_frame.ltitle -text [::msgcat::mc "Title:"]
+    [entry $dialog_frame.title] insert end $title
+    label $dialog_frame.ltags  -text [::msgcat::mc "Tags:"]
+    [entry $dialog_frame.tags] insert end $tags_str
+
+    set sw [ScrolledWindow $dialog_frame.sw]
+    [textUndoable $dialog_frame.text -wrap word] insert end $text
+    $sw setwidget $dialog_frame.text
+
+    grid $dialog_frame.ltitle -row 0 -column 0 -sticky nw
+    grid $dialog_frame.title  -row 0 -column 1 -sticky ew
+    grid $dialog_frame.ltags  -row 1 -column 0 -sticky nw
+    grid $dialog_frame.tags   -row 1 -column 1 -sticky ew
+    grid $sw -row 2 -column 0 -columnspan 2 -sticky nw
+
+# TODO: replace ugly get_real_index with notes id.
+    $dialog_w add -text [::msgcat::mc "Ok"] \
+        -command [list [namespace current]::edit_note_cmd_ok $dialog_w $idx]
+    $dialog_w add -text [::msgcat::mc "Cancel"] \
+        -command [list destroy $dialog_w]
+
+# Next 3 commands from ${PATH_TO_TKABBER}/plugins/roster/annotations.tcl (annotations::show_dialog).
+    bind $dialog_frame.text <Control-Key-Return> "[double% $dialog_w] invoke default
+                                       break"
+    bind $dialog_w <Key-Return> { }
+    bind $dialog_w <Control-Key-Return> "[double% $dialog_w] invoke default
+                                  break"
+
+    $dialog_w draw $dialog_frame.title
+}
+
+proc ifaces::gui::edit_note_cmd_ok {dialog_w idx} {
+    set dialog_frame [$dialog_w getframe]
+
+    set title [$dialog_frame.title get]
+    set tags_str [$dialog_frame.tags get]
+    set text [$dialog_frame.text get 0.0 "end -1 char"]
+
+    set new_note [::xmpp::private::notes::create $title "" $text $tags_str]
+
+    ::plugins::notes::set_note [::plugins::notes::get_real_index $idx [get_filter_tags]] $idx $new_note
+    destroy $dialog_w
+}
+
+proc ifaces::gui::get_filter_tags {} {
+    set tags .notes.tools.tags
+
+    return [split [string trim [$tags get] " "] " "]
+}
+
+proc ifaces::gui::update_lbox_at {idx new_note} {
+    set lbox .notes.lbox_frame.lbox
+
+    if {![cequal $idx end]} {
+        $lbox delete $idx $idx
+    }
+
+    if {[expr [llength $new_note] != 0] &&
+        [::plugins::notes::filter $new_note [get_filter_tags]]} {
+        $lbox insert $idx [get_short_string $new_note]
+    }
+
+    update_lbox_selection $idx
+}
+
+proc ifaces::gui::update_lbox {} {
+    set lbox .notes.lbox_frame.lbox
+
+    $lbox delete 0 end
+
+    foreach note [::plugins::notes::get_notes [get_filter_tags]] {
+        $lbox insert end [get_short_string $note]
+    }
+
+    update_lbox_selection end
+}
+
+proc ifaces::gui::update_lbox_selection {idx} {
+    set lbox .notes.lbox_frame.lbox
+
+    $lbox selection clear 0 end
+    $lbox selection set $idx
+    $lbox activate $idx
+}
+
+proc ifaces::gui::export_notes_window {} {
+    if {[llength [plugins::notes::connections]] == 0} return
+
+    set dialog_w .export_notes
+    catch { destroy $dialog_w }
+    set dialog_w [Dialog $dialog_w -title [::msgcat::mc "Export"] \
+        -separator 1 \
+        -anchor e \
+        -default 0 \
+        -cancel 1 \
+        -modal none]
+
+    set dialog_frame [$dialog_w getframe]
+    grid columnconfigure $dialog_frame 1 -weight 1
+
+    set file_label [label $dialog_frame.lfile -text [::msgcat::mc "File:"]]
+    set file_entry [entry $dialog_frame.file]
+
+    grid $file_label -row 0 -column 0 -sticky nw
+    grid $file_entry  -row 0 -column 1 -sticky ew
+
+    $dialog_w add -text [::msgcat::mc "Ok"] \
+        -command [list [namespace current]::export_notes_cmd_ok $dialog_w]
+    $dialog_w add -text [::msgcat::mc "Cancel"] \
+        -command [list destroy $dialog_w]
+
+    bind $file_entry <Control-Key-Return> \
+        "[double% $dialog_w] invoke default
+        break"
+    bind $dialog_w <Control-Key-Return> \
+        "[double% $dialog_w] invoke default
+        break"
+
+    $dialog_w draw $file_entry
+}
+
+proc ifaces::gui::export_notes_cmd_ok {dialog_w} {
+    set dialog_frame [$dialog_w getframe]
+
+    set notes_file [$dialog_frame.file get]
+
+    ::plugins::notes::ie::export $notes_file [get_filter_tags]
+    destroy $dialog_w
+}
+
+####################################################################
+# Utils
+
+proc ifaces::gui::get_short_string {note} {
+    ::xmpp::private::notes::split $note title -> text tags_str
+
+    set short_string $title
+
+    if {[string length $short_string] == 0} {
+        set short_string $text
+    }
+
+    if {[string length $short_string] == 0} {
+        set short_string {[Title and text is empty]}
+    }
+
+# TODO: Need?
+if {0} {
+    set max_length 30
+    if {[string length $short_string] > $max_length} {
+        set short_string "[string range $short_string 0 [expr $max_length - 4]]..."
+    }
+}
+
+    return $short_string
+}

Modified: trunk/plugins/notes/notes.tcl
===================================================================
--- trunk/plugins/notes/notes.tcl	2011-07-13 19:08:21 UTC (rev 491)
+++ trunk/plugins/notes/notes.tcl	2011-08-03 11:55:52 UTC (rev 492)
@@ -17,141 +17,93 @@
     }
 }
 
+####################################################################
+# Load/unload
+
 proc notes::load {} {
-    variable notes
-    array set notes {}
+    variable scriptdir
+# We need initializing it?
+    free_all_notes
 
-    hook::add finload_hook [namespace current]::setup_menu
+    set ifaces_dir [file join $scriptdir ifaces]
+    foreach iface_file [glob -nocomplain -directory "$ifaces_dir" "*.tcl"] {
+# TODO: We need source $iface_file?
+        source $iface_file
+        set iface_name [file tail [file rootname $iface_file]]
+        eval ifaces::${iface_name}::load
+    }
+
+    source [file join $scriptdir ie.tcl]
+
     hook::add disconnected_hook [namespace current]::disconnected
     hook::add connected_hook [namespace current]::connected
 
-    if {[llength [connections]] > 0} {
-        request_notes [lindex [connections 0]]
-    }
-
-    setup_menu
+    request_all_notes
 }
 
 proc notes::unload {} {
-    hook::remove finload_hook [namespace current]::setup_menu
-    hook::remove disconnected_hook [namespace current]::disconnected
-    hook::remove connected_hook [namespace current]::connected
-    desetup_menu
-    destroy_win .notes
-    free_all_notes
-}
+    variable scriptdir
 
-proc notes::open_window {} {
-    set w .notes
-
-    if {[winfo exists $w]} {
-        raise_win $w
-        return
+# TODO: think about create list of ifaces on load (in notes::load) and unload them by this list
+    set ifaces_dir [file join $scriptdir ifaces]
+    foreach iface_file [glob -nocomplain -directory "$ifaces_dir" "*.tcl"] {
+        set iface_name [file tail [file rootname $iface_file]]
+        catch { eval ifaces::${iface_name}::unload }
     }
 
-#    if {[llength [connections]] == 0} return
 
-    add_win $w -title [::msgcat::mc "Notes"] \
-        -tabtitle [::msgcat::mc "Notes"] \
-        -class Notes \
-        -raise 1
+    hook::remove disconnected_hook [namespace current]::disconnected
+    hook::remove connected_hook [namespace current]::connected
 
-    create_notes_tab $w
-
-    # TODO: store notes immediately after edit (now)
-    # or after close tab (see next line).
-#    bind $w <Destroy> +[list [namespace current]::store_all_notes]
+    free_all_notes
 }
 
-# ############################
-# Register in the main menu
+####################################################################
+# Connect/disconnect
 
-proc notes::setup_menu {} {
-    catch {
-        set m [.mainframe getmenu plugins]
-        set ind [$m index end]
-
-        $m insert $ind command -label [::msgcat::mc "Notes"] \
-            -command [list [namespace current]::open_window]
-    }
+proc notes::disconnected {xlib} {
+    free_notes $xlib
 }
 
-proc notes::desetup_menu {} {
-    catch {
-        set m [.mainframe getmenu plugins]
-        set ind [$m index [::msgcat::mc "Notes"]]
-
-        $m delete $ind
-    }
+proc notes::connected {xlib} {
+    request_notes $xlib
 }
 
-##############################################################################
+####################################################################
+# Request notes
 
-proc notes::free_all_notes {} {
-    variable notes
-    variable current_xlib
-
-    catch { unset current_xlib }
-    array unset notes
-
-    update_gui
-}
-
-proc notes::free_notes {xlib} {
-    variable notes
-    variable current_xlib
-
-    if {[info exists current_xlib]} {
-        if {[cequal $current_xlib $xlib]} {
-            if {[llength [connections]] > 0} {
-                set current_xlib [lindex [connections] 0]
-            } else {
-                catch { unset current_xlib }
-                # TODO: need unset if {![cequal $current_xlib $xlib]} ?
-            }
-        }
-    }
-
-    array unset notes $xlib
-    update_gui
-}
-
 proc notes::request_notes {xlib} {
     ::xmpp::private::notes::retrieve $xlib \
         -command [list [namespace current]::process_notes $xlib]
 }
 
-proc notes::disconnected {xlib} {
-    free_notes $xlib
-}
-
-proc notes::connected {xlib} {
-    request_notes $xlib
-}
-
 proc notes::process_notes {xlib status noteslist} {
     variable notes
     variable current_xlib
 
     if {$status != "ok"} return
 
+ #TODO( maybe not sending event?
     free_notes $xlib
+#)
     set notes($xlib) $noteslist
+    hook::run plugins_notes_changed_connections_hook
+
     if {![info exist current_xlib]} {
-        set current_xlib $xlib
+        set_current_xlib $xlib
     }
+}
 
-    if {[winfo exists .notes]} {
-        update_connections_menu
-        update_connections_menu_label
-        if {[cequal $current_xlib $xlib]} {
-            update_lbox
-        }
+proc notes::request_all_notes {} {
+    # Request notes for all established connections.
+    foreach conn [::connections] {
+        request_notes $conn
     }
-
-    return
 }
 
+####################################################################
+# Store notes
+
 proc notes::store_notes {xlib} {
     variable notes
 
@@ -165,245 +117,172 @@
 
 proc notes::store_all_notes {} {
     variable notes
-    foreach xlib [array names notes] {
-        store_notes $xlib
+
+    foreach conn [connections] {
+        store_notes $conn
     }
 }
 
-##############################################################################
+####################################################################
+# Free notes
 
-proc notes::create_notes_tab {w} {
+#proc notes::free_notes {xlib {send_event 1}} {#TODO: need? 
+proc notes::free_notes {xlib} {
+    variable notes
     variable current_xlib
 
-    # button box
-    set tools [frame $w.tools -borderwidth 5]
-    pack $tools -side top -fill y -anchor w
+    array unset notes $xlib
+    hook::run plugins_notes_changed_connections_hook
+# TODO: possibly, comfortable variant:
+# hook::run plugins_notes_changed_connections_hook $xlib
+# or
+# hook::run plugins_notes_changed_connections_hook [connections]
+# also, see all proc, where run this hook and all proc, added to this hook in gui.tcl
 
-    set new_button [button $tools.new_button -text [::msgcat::mc "New note"] \
-        -command [list [namespace current]::edit_note -1]]
-    pack $new_button -anchor w -side left
-    set delete_button [button $tools.delete_button -text [::msgcat::mc "Delete note"] \
-        -command [list [namespace current]::delete_focused_note]]
-    pack $delete_button -anchor w -side left
+    if {[info exists current_xlib]} {
+        if {[cequal $current_xlib $xlib]} {
+            if {[llength [connections]] > 0} {
+                set_current_xlib [lindex [connections] 0]
+            } else {
+                unset_current_xlib
+            }
+#TODO: need unset current_xlib if {![cequal $current_xlib $xlib]} ?
+        }
+    }
+}
 
-# TODO: from menubutton to OptionMenu?
-#    set conn_menu [OptionMenu $tools.conn_menu [connections]]
+proc notes::free_all_notes {} {
+    variable notes
 
-    # ==== List of connections ====
-    set conn_button [menubutton $tools.conn_button \
-        -menu $tools.conn_button.menu]
-    set conn_menu [menu $conn_button.menu -tearoff 0]
-    update_connections_menu
-    update_connections_menu_label
-    pack $conn_button -anchor w -side left
-    # ==== ====
+    unset_current_xlib
+    array unset notes
+    hook::run plugins_notes_changed_connections_hook
+}
 
-    # main frame
-    set lbox_frame [frame $w.lbox_frame]
-    pack $lbox_frame -side left -fill both -expand true -anchor w
+####################################################################
+# Other utils
 
-    # main frame widget
-    grid columnconfigure $lbox_frame 0 -weight 1
-    set sw [ScrolledWindow $lbox_frame.sw]
+# We know about connections, which relate to not empty notes list
+proc notes::connections {} {
+    variable notes
 
-    set lbox [listbox $lbox_frame.lbox -takefocus 1 -exportselection 0]
-    $lbox selection clear 0 end
-    update_lbox
-    $lbox selection set 0
-    focus $lbox
+    return [lsort [array names notes]]
+}
 
-# ==== Bind ====
-    # From ${PATH_TO_TKABBER}/plugins/chat/histool.tcl
-    # Workaround for a bug in listbox (can't get focus on mouse clicks):
-    bind Listbox <Button-1> {+ if {[winfo exists %W]} {focus %W}}
+#proc notes::is_current_xlib {xlib} {}
 
-    bind $lbox <Double-Button-1> [namespace code {
-        edit_note [%W nearest %y]
-    }]
+####################################################################
+# Interaction with ifaces
+# All proc work with current_xlib
 
-    bind $lbox <Return> [namespace code {
-        edit_note [%W index active]
-    }]
-# ====  ====
+proc notes::set_current_xlib {xlib} {
+    variable current_xlib
 
-    $sw setwidget $lbox
-    grid $sw -sticky news
-    grid rowconfigure $lbox_frame 0 -weight 1
+    set current_xlib $xlib
+    hook::run plugins_notes_changed_current_connection_hook
 }
 
-proc notes::update_gui {} {
-    if {[winfo exists .notes]} {
-        update_connections_menu
-        update_connections_menu_label
-        update_lbox
-   }
+proc notes::unset_current_xlib {} {
+    variable current_xlib
+
+    if {![info exists current_xlib]} return
+
+    unset current_xlib
+    hook::run plugins_notes_changed_current_connection_hook
 }
 
-proc notes::update_connections_menu_label {} {
+proc notes::get_current_connection_name {} {
     variable current_xlib
-    set conn_button .notes.tools.conn_button
 
     if {[info exists current_xlib]} {
-        set text [connection_jid $current_xlib]
+        set connection_name [connection_jid $current_xlib]
     } else {
-        set text "Not connected"
+        set connection_name [::msgcat::mc "Disconnected"]
     }
 
-    $conn_button configure -text $text
+    return $connection_name
 }
 
-proc notes::update_connections_menu {} {
-    set conn_menu .notes.tools.conn_button.menu
-    $conn_menu delete 0 end
-
-    foreach conn [connections] {
-        $conn_menu add command \
-            -label [connection_jid $conn] \
-            -command [namespace code [format {
-                variable current_xlib
-                set current_xlib %1$s
-                update_connections_menu_label
-                update_lbox} $conn]]
-    }
-}
-
-proc notes::delete_focused_note {} {
-#TODO: ask "Are you sure..?".
+proc notes::get_note {idx {search_tags {}}} {
     variable notes
     variable current_xlib
 
-    set lbox .notes.lbox_frame.lbox
-    set idx [$lbox index active]
-    set notes($current_xlib) [lreplace $notes($current_xlib) $idx $idx]
-    store_notes $current_xlib
-    $lbox delete $idx $idx
+#    if {![info exists current_xlib]} return # Hm... TODO: think.
+
+    return [lindex [get_notes $search_tags] $idx]
 }
 
-proc notes::edit_note {idx args} {
+proc notes::set_note {real_idx idx new_note} {
     variable notes
     variable current_xlib
 
-    if {$idx >= 0} {
-        set source_note [lindex $notes($current_xlib) $idx]
-        ::xmpp::private::notes::split $source_note title -> text tags_str
+    if {![info exists current_xlib]} return
+
+    if {[llength $new_note] == 0} {
+        set notes($current_xlib) [lreplace $notes($current_xlib) $real_idx $real_idx]
     } else {
-        set title ""
-        set tags_str ""
-        set text ""
+        if {[cequal $real_idx end]} {
+            lappend notes($current_xlib) $new_note
+        } else {
+            lset notes($current_xlib) $real_idx $new_note
+        }
     }
 
-    set dialog_w .edit_note
-    catch { destroy $dialog_w }
-    set dialog_w [Dialog $dialog_w -title [::msgcat::mc "Edit note"] \
-        -separator 1 \
-        -anchor e \
-        -default 0 \
-        -cancel 1 \
-        -modal none]
-
-    set dialog_frame [$dialog_w getframe]
-    grid columnconfigure $dialog_frame 1 -weight 1
-
-    label $dialog_frame.ltitle -text [::msgcat::mc "Title:"]
-    [entry $dialog_frame.title] insert end $title
-    label $dialog_frame.ltags  -text [::msgcat::mc "Tags:"]
-    [entry $dialog_frame.tags] insert end $tags_str
-
-    set sw [ScrolledWindow $dialog_frame.sw]
-    [textUndoable $dialog_frame.text -wrap word] insert end $text
-    $sw setwidget $dialog_frame.text
-
-    grid $dialog_frame.ltitle -row 0 -column 0 -sticky nw
-    grid $dialog_frame.title  -row 0 -column 1 -sticky ew
-    grid $dialog_frame.ltags  -row 1 -column 0 -sticky nw
-    grid $dialog_frame.tags   -row 1 -column 1 -sticky ew
-    grid $sw -row 2 -column 0 -columnspan 2 -sticky nw
-
-    $dialog_w add -text [::msgcat::mc "Ok"] \
-        -command [list [namespace current]::edit_note_cmd_ok $dialog_w $idx $current_xlib]
-    $dialog_w add -text [::msgcat::mc "Cancel"] \
-        -command [list destroy $dialog_w]
-
-# Next 3 commands from ${PATH_TO_TKABBER}/plugins/roster/annotations.tcl (annotations::show_dialog).
-    bind $dialog_frame.text <Control-Key-Return> "[double% $dialog_w] invoke default
-                                       break"
-    bind $dialog_w <Key-Return> { }
-    bind $dialog_w <Control-Key-Return> "[double% $dialog_w] invoke default
-                                  break"
-
-    $dialog_w draw $dialog_frame.title
+    store_notes $current_xlib
+    hook::run plugins_notes_changed_note_hook $idx $new_note
 }
 
-proc notes::edit_note_cmd_ok {dialog_w idx xlib} {
-    variable notes
+proc notes::filter {note search_tags} {
+    ::xmpp::private::notes::split $note title tags text tags_str
 
-    set dialog_frame [$dialog_w getframe]
-
-    set title [$dialog_frame.title get]
-    set tags_str [$dialog_frame.tags get]
-    set text [$dialog_frame.text get 0.0 "end -1 char"]
-
-    set new_note [::xmpp::private::notes::create $title "" $text $tags_str]
-
-    if {$idx >= 0} {
-        lset notes($xlib) $idx $new_note
-        store_notes $xlib
-        update_lbox_at $idx $new_note
-    } else {
-        lappend notes($xlib) $new_note
-        store_notes $xlib
-        append_to_lbox $new_note
+    foreach search_tag $search_tags {
+        if {[lsearch -exact $tags $search_tag] < 0} {
+            return 0
+        }
     }
-    destroy $dialog_w
-}
 
-proc notes::update_lbox_at {idx new_note} {
-    set lbox .notes.lbox_frame.lbox
-    $lbox delete $idx $idx
-    $lbox insert $idx [get_short_string $new_note]
+    return 1
 }
 
-proc notes::append_to_lbox {new_note} {
-    set lbox .notes.lbox_frame.lbox
-    $lbox insert end [get_short_string $new_note]
-}
-
-proc notes::update_lbox {} {
+proc notes::get_notes {{search_tags {}}} {
     variable notes
     variable current_xlib
 
-    set lbox .notes.lbox_frame.lbox
-    $lbox delete 0 end
+    set current_notes {}
 
-    if {[info exists current_xlib]} {
+    if {![info exists current_xlib]} {
+        return $current_notes
+    }
+
+    if {[llength $search_tags] == 0} {
+        set current_notes $notes($current_xlib)
+    } else {
         foreach note $notes($current_xlib) {
-            $lbox insert end [get_short_string $note]
+            if {[filter $note $search_tags]} {
+                lappend current_notes $note
+            }
         }
     }
-#    $lbox selection clear 0 end
-#    $lbox selection set 0
+
+    return $current_notes
 }
 
-proc notes::get_short_string {note} {
-    ::xmpp::private::notes::split $note title -> text tags_str
-
-    set short_string $title
-
-    if {[string length $short_string] == 0} {
-        set short_string $text
+# TODO: replace ugly get_real_index with notes id.
+proc notes::get_real_index {idx {search_tags {}}} {
+    if {[cequal $idx end]} {
+        return end
     }
 
-    if {[string length $short_string] == 0} {
-        set short_string {[Title and text is empty]}
+    set real_index 0
+    foreach note [get_notes] {
+        if {[filter $note $search_tags]} {
+            incr idx -1
+        }
+        if {$idx < 0} {
+            return $real_index
+        }
+        incr real_index
     }
 
-# TODO: Need?
-if {0} {
-    set max_length 30
-    if {[string length $short_string] > $max_length} {
-        set short_string "[string range $short_string 0 [expr $max_length - 4]]..."
-    }
+    return -1
 }
-
-    return $short_string
-}

Deleted: trunk/plugins/notes/proto
===================================================================
--- trunk/plugins/notes/proto	2011-07-13 19:08:21 UTC (rev 491)
+++ trunk/plugins/notes/proto	2011-08-03 11:55:52 UTC (rev 492)
@@ -1,63 +0,0 @@
-See XEP-0049.
-
-===================
-Request to receive notes:
-===================
-
-<iq type='get' id='id1'>
-    <query xmlns='jabber:iq:private'>
-        <storage xmlns='http://miranda-im.org/storage#notes'/>
-    </query>
-</iq>
-
-===================
-Positive response:
-===================
-
-<iq type='result' to='myjid at server.tld' id='id1'>
-    <query xmlns='jabber:iq:private'>
-        <storage xmlns='http://miranda-im.org/storage#notes'>
-
-            <note tags='tag1 tag2 tag3'>
-                <title>Title 1</title>
-                <text>Note body 1</text>
-            </note>
-
-            <note tags=''>
-                <title>Title 2</title>
-                <text>Note body 2</text>
-            </note>
-
-        </storage>
-    </query>
-</iq>
-
-===================
-Send notes to server:
-===================
-
-<iq type='set' id='id1'>
-    <query xmlns='jabber:iq:private'>
-        <storage xmlns='http://miranda-im.org/storage#notes'>
-	
-            <note tags='tag1 tag2 tag3'>
-                <title>Title 1</title>
-                <text>Note body</text>
-            </note>
-
-            <note tags=''>
-                <title>Title 2</title>
-                <text>Note body 2</text>
-            </note>
-
-        </storage>
-    </query>
-</iq>
-
-===================
-Positive response:
-===================
-
-<iq type='result' to='myjid at server.tld' id='id1'>
-    <query xmlns='jabber:iq:private'/>
-</iq>



More information about the Tkabber-dev mailing list