[polari] app: Implement show-message-user-dialog action



commit b3ba2ef5a59a3bb7be139b2a114c12098dc84dff
Author: Florian Müllner <fmuellner gnome org>
Date:   Sat Sep 28 22:22:37 2013 +0200

    app: Implement show-message-user-dialog action
    
    The corresponding menu item has been around since forever, but without
    doing anything - let's change that! Add a simple dialog similar to the
    join-room one, but include a small list of recently contacted nicks
    for convenience.

 data/polari.gresource.xml             |    1 +
 data/resources/message-user-dialog.ui |  231 +++++++++++++++++++++++++++++++++
 po/POTFILES.in                        |    2 +
 src/Makefile.am                       |    1 +
 src/application.js                    |    6 +-
 src/mainWindow.js                     |   11 ++
 src/messageDialog.js                  |  171 ++++++++++++++++++++++++
 7 files changed, 421 insertions(+), 2 deletions(-)
---
diff --git a/data/polari.gresource.xml b/data/polari.gresource.xml
index 7ecc781..2e2e819 100644
--- a/data/polari.gresource.xml
+++ b/data/polari.gresource.xml
@@ -6,6 +6,7 @@
     <file alias="connection-list-dialog.ui" 
preprocess="xml-stripblanks">resources/connection-list-dialog.ui</file>
     <file alias="join-room-dialog.ui" preprocess="xml-stripblanks">resources/join-room-dialog.ui</file>
     <file alias="main-window.ui" preprocess="xml-stripblanks">resources/main-window.ui</file>
+    <file alias="message-user-dialog.ui" preprocess="xml-stripblanks">resources/message-user-dialog.ui</file>
     <file alias="application.css">resources/application.css</file>
   </gresource>
 </gresources>
diff --git a/data/resources/message-user-dialog.ui b/data/resources/message-user-dialog.ui
new file mode 100644
index 0000000..285ae12
--- /dev/null
+++ b/data/resources/message-user-dialog.ui
@@ -0,0 +1,231 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.15.4 on Sat Sep 28 22:12:51 2013 -->
+<interface>
+  <!-- interface-requires gtk+ 3.10 -->
+  <object class="GtkListStore" id="liststore1">
+    <columns>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkEntryCompletion" id="name_completion">
+    <property name="model">liststore1</property>
+    <property name="text_column">0</property>
+    <property name="inline_completion">True</property>
+    <property name="popup_single_match">False</property>
+    <child>
+      <object class="GtkCellRendererText" id="cellrenderertext1"/>
+      <attributes>
+        <attribute name="text">0</attribute>
+      </attributes>
+    </child>
+  </object>
+  <object class="GtkDialog" id="message_user_dialog">
+    <property name="can_focus">False</property>
+    <property name="title" translatable="yes">Message User</property>
+    <property name="modal">True</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox1">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area1">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="cancel_button">
+                <property name="label" translatable="yes">_Cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="message_button">
+                <property name="label" translatable="yes">_Message</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="has_default">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkGrid" id="grid1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="halign">center</property>
+            <property name="margin_left">96</property>
+            <property name="margin_right">96</property>
+            <property name="margin_top">24</property>
+            <property name="margin_bottom">48</property>
+            <property name="row_spacing">12</property>
+            <property name="column_spacing">12</property>
+            <child>
+              <object class="GtkLabel" id="server_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Server</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+                <property name="width">2</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="connection_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">end</property>
+                <property name="label" translatable="yes">C_onnection</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">connection_combo</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">1</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="user_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_top">24</property>
+                <property name="label" translatable="yes">User</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">2</property>
+                <property name="width">2</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBoxText" id="connection_combo">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">1</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="recent_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">end</property>
+                <property name="valign">start</property>
+                <property name="label" translatable="yes">_Recent</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">recent_list</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">3</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFrame" id="frame1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">in</property>
+                <child>
+                  <object class="GtkListBox" id="recent_list">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="selection_mode">none</property>
+                  </object>
+                </child>
+                <child type="label_item">
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">3</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="name_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">end</property>
+                <property name="label" translatable="yes">_Name</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">name_entry</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">4</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="name_entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="activates_default">True</property>
+                <property name="completion">name_completion</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">4</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">cancel_button</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index af32d25..d3598f2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -5,8 +5,10 @@ data/org.gnome.Polari.desktop.in
 [type: gettext/glade]data/resources/connection-list-dialog.ui
 [type: gettext/glade]data/resources/join-room-dialog.ui
 [type: gettext/glade]data/resources/main-window.ui
+[type: gettext/glade]data/resources/message-user-dialog.ui
 src/application.js
 src/chatView.js
 src/connections.js
 src/mainWindow.js
+src/messageDialog.js
 src/userList.js
diff --git a/src/Makefile.am b/src/Makefile.am
index ec8845e..da04646 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -37,6 +37,7 @@ dist_js_DATA = \
        joinDialog.js \
        main.js \
        mainWindow.js \
+       messageDialog.js \
        notify.js \
        pasteManager.js \
        roomList.js \
diff --git a/src/application.js b/src/application.js
index 16931e1..8b15943 100644
--- a/src/application.js
+++ b/src/application.js
@@ -75,7 +75,9 @@ const Application = new Lang.Class({
             create_hook: Lang.bind(this, this._accountActionsCreateHook),
             accel: '<Primary>n' },
           { name: 'show-message-user-dialog',
-            activate: Lang.bind(this, this._onShowMessageUserDialog) },
+            activate: Lang.bind(this, this._onShowMessageUserDialog),
+            create_hook: Lang.bind(this, this._accountActionsCreateHook),
+            accel: '<Primary>m' },
           { name: 'join-room',
             activate: Lang.bind(this, this._onJoinRoom),
             parameter_type: GLib.VariantType.new('(ssu)') },
@@ -230,7 +232,7 @@ const Application = new Lang.Class({
     },
 
     _onShowMessageUserDialog: function() {
-        log('Activated action "Message user"');
+        this._window.showMessageUserDialog();
     },
 
     _addSavedChannel: function(account, channel) {
diff --git a/src/mainWindow.js b/src/mainWindow.js
index dbec756..3842195 100644
--- a/src/mainWindow.js
+++ b/src/mainWindow.js
@@ -12,6 +12,7 @@ const IrcParser = imports.ircParser;
 const JoinDialog = imports.joinDialog;
 const Lang = imports.lang;
 const Mainloop = imports.mainloop;
+const MessageDialog = imports.messageDialog;
 const RoomList = imports.roomList;
 const TabCompletion = imports.tabCompletion;
 const UserList = imports.userList;
@@ -356,6 +357,16 @@ const MainWindow = new Lang.Class({
             });
     },
 
+    showMessageUserDialog: function() {
+        let dialog = new MessageDialog.MessageDialog();
+        dialog.widget.transient_for = this.window;
+        dialog.widget.show();
+        dialog.widget.connect('response',
+            function(widget) {
+                widget.destroy();
+            });
+    },
+
     _updateCompletions: function() {
         let nicks = [];
 
diff --git a/src/messageDialog.js b/src/messageDialog.js
new file mode 100644
index 0000000..bd130bd
--- /dev/null
+++ b/src/messageDialog.js
@@ -0,0 +1,171 @@
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Gtk = imports.gi.Gtk;
+const Tp = imports.gi.TelepathyGLib;
+const Tpl = imports.gi.TelepathyLogger;
+
+const AccountsMonitor = imports.accountsMonitor;
+const Lang = imports.lang;
+
+const TP_CURRENT_TIME = GLib.MAXUINT32;
+const MAX_RECENT_USERS = 5;
+
+const MessageDialog = new Lang.Class({
+    Name: 'MessageDialog',
+
+    _init: function() {
+        this._createWidget();
+
+        this._accounts = {};
+        AccountsMonitor.getDefault().dupAccounts().forEach(Lang.bind(this,
+            function(a) {
+                if (!a.enabled)
+                    return;
+                this._accounts[a.display_name] = a;
+            }));
+        let names = Object.keys(this._accounts).sort(
+            function(a, b) {
+                // TODO: figure out combo box sorting
+                return (a < b) ? -1 : ((a > b) ? 1 : 0);
+            });
+        for (let i = 0; i < names.length; i++)
+            this._connectionCombo.append_text(names[i]);
+        this._connectionCombo.set_active(0);
+        this._connectionCombo.sensitive = names.length > 1;
+        this._updateCanConfirm();
+        this._updateRecentList([]);
+    },
+
+    _createWidget: function() {
+        let builder = new Gtk.Builder();
+        builder.add_from_resource('/org/gnome/polari/message-user-dialog.ui');
+
+        this.widget = builder.get_object('message_user_dialog');
+
+        this._connectionCombo = builder.get_object('connection_combo');
+        this._connectionCombo.connect('changed',
+                                      Lang.bind(this, this._onAccountChanged));
+        this._connectionCombo.sensitive = false;
+
+        this._recentList = builder.get_object('recent_list');
+        this._recentList.connect('row-activated', Lang.bind(this,
+            function(w, row) {
+                this._nameEntry.text = row.name;
+            }));
+        this._nameCompletion = builder.get_object('name_completion');
+
+        this._messageButton = builder.get_object('message_button');
+        this._messageButton.connect('clicked',
+                                 Lang.bind(this, this._onMessageClicked));
+        this._messageButton.sensitive = false;
+
+        this._nameEntry = builder.get_object('name_entry');
+        this._nameEntry.connect('changed',
+                                Lang.bind(this, this._updateCanConfirm));
+    },
+
+    _updateRecentList: function(names) {
+        this._recentList.foreach(Lang.bind(this,
+            function(row) {
+                row.destroy();
+            }));
+
+        if (names.length == 0) {
+            let row = new Gtk.ListBoxRow({ sensitive: false });
+            this._recentList.add(row);
+
+            let text = '<i>%s</i>'.format(_("No recent users"));
+            let label = new Gtk.Label({ label: text,
+                                        use_markup: true });
+            row.add(label);
+            row.show_all();
+        }
+
+        for (let i = 0; i < names.length; i++) {
+            let model = this._nameCompletion.model;
+            let iter = model.append();
+            model.set_value(iter, 0, names[i]);
+
+            if (i >= MAX_RECENT_USERS)
+                continue;
+
+            let row = new Gtk.ListBoxRow();
+            row.name = names[i];
+
+            let box = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL,
+                                    spacing: 6 });
+            row.add(box);
+
+            box.add(new Gtk.Image({ icon_name: 'avatar-default-symbolic',
+                                    icon_size: Gtk.IconSize.MENU,
+                                    valign: Gtk.Align.BASELINE }));
+           box.add(new Gtk.Label({ label: row.name,
+                                   valign: Gtk.Align.BASELINE }));
+           row.show_all();
+
+           this._recentList.add(row);
+        }
+    },
+
+    _onAccountChanged: function() {
+        this._nameEntry.set_text('');
+        this._nameCompletion.model.clear();
+
+        let selected = this._connectionCombo.get_active_text();
+        let account = this._accounts[selected];
+        let logManager = Tpl.LogManager.dup_singleton();
+
+        logManager.get_entities_async(account, Lang.bind(this,
+            function(m, res) {
+                let [, entities] = logManager.get_entities_finish(res);
+                entities = entities.filter(function(e) {
+                    return e.type == Tpl.EntityType.CONTACT;
+                });
+                let pending = entities.length;
+                if (pending == 0) {
+                    this._updateRecentList([]);
+                    return;
+                }
+                for (let i = 0; i < entities.length; i++) {
+                    let entity = entities[i];
+                    logManager.get_filtered_events_async(account, entity,
+                        Tpl.EventTypeMask.TEXT, 1, null,
+                        Lang.bind(this, function(m, res) {
+                            let [, events] = m.get_filtered_events_finish(res);
+                            entity._timestamp = events[0].timestamp;
+                            if (--pending > 0)
+                                return;
+                            let names = entities.sort(function(a, b) {
+                                return b._timestamp - a._timestamp;
+                            }).map(function(e) {
+                                return e.alias;
+                            });
+                            this._updateRecentList(names);
+                        }));
+                }
+            }));
+    },
+
+    _onMessageClicked: function() {
+        this.widget.hide();
+
+        let selected = this._connectionCombo.get_active_text();
+        let account = this._accounts[selected];
+
+        let user = this._nameEntry.get_text();
+
+        let app = Gio.Application.get_default();
+        let action = app.lookup_action('message-user');
+        action.activate(GLib.Variant.new('(ssu)',
+                                         [ account.get_object_path(),
+                                           user,
+                                           TP_CURRENT_TIME ]));
+        this.widget.response(Gtk.ResponseType.OK);
+    },
+
+    _updateCanConfirm: function() {
+            let sensitive = this._connectionCombo.get_active() > -1  &&
+                            this._nameEntry.get_text_length() > 0;
+            this._messageButton.sensitive = sensitive;
+    }
+});


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]