~ps-jenkins/indicator-transfer/ubuntu-vivid-proposed

« back to all changes in this revision

Viewing changes to src/menu.cpp

  • Committer: Charles Kerr
  • Date: 2014-06-17 01:36:16 UTC
  • mto: This revision was merged to the branch mainline in revision 2.
  • Revision ID: charles.kerr@canonical.com-20140617013616-7fcd22wh3hbgaovh
code drop

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright 2013 Canonical Ltd.
3
 
 *
4
 
 * This program is free software: you can redistribute it and/or modify it
5
 
 * under the terms of the GNU General Public License version 3, as published
6
 
 * by the Free Software Foundation.
7
 
 *
8
 
 * This program is distributed in the hope that it will be useful, but
9
 
 * WITHOUT ANY WARRANTY; without even the implied warranties of
10
 
 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11
 
 * PURPOSE.  See the GNU General Public License for more details.
12
 
 *
13
 
 * You should have received a copy of the GNU General Public License along
14
 
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
15
 
 *
16
 
 * Authors:
17
 
 *   Charles Kerr <charles.kerr@canonical.com>
18
 
 */
19
 
 
20
 
#include <transfer/menu.h>
21
 
 
22
 
#include <transfer/gactions.h>
23
 
#include <transfer/transfer.h>
24
 
 
25
 
#include <glib/gi18n.h>
26
 
#include <gio/gio.h>
27
 
 
28
 
#include <memory> // shared_ptr
29
 
#include <vector>
30
 
 
31
 
namespace unity {
32
 
namespace indicator {
33
 
namespace transfer {
34
 
 
35
 
/****
36
 
*****
37
 
****/
38
 
 
39
 
Menu::Menu (Profile profile_in, const char* name_in):
40
 
    m_profile{profile_in},
41
 
    m_name{name_in}
42
 
{
43
 
}
44
 
 
45
 
const char* Menu::name() const
46
 
{
47
 
    return m_name;
48
 
}
49
 
 
50
 
Menu::Profile Menu::profile() const
51
 
{
52
 
    return m_profile;
53
 
}
54
 
 
55
 
GMenuModel* Menu::menu_model()
56
 
{
57
 
    Transfers tmp;
58
 
 
59
 
    return G_MENU_MODEL(m_menu);
60
 
}
61
 
 
62
 
/****
63
 
*****
64
 
****/
65
 
 
66
 
class MenuImpl: public Menu
67
 
{
68
 
protected:
69
 
    MenuImpl(const Menu::Profile profile_in,
70
 
             const char* name_in,
71
 
             const std::shared_ptr<Transfers>& transfers,
72
 
             const std::shared_ptr<GActions>& gactions):
73
 
        Menu(profile_in, name_in),
74
 
        m_transfers(transfers),
75
 
        m_gactions(gactions)
76
 
    {
77
 
        // initialize the menu
78
 
        create_gmenu();
79
 
        for (int i=0; i<(int)NUM_SECTIONS; i++)
80
 
            update_section(Section(i));
81
 
 
82
 
        // listen for state changes so we can update the menu accordingly
83
 
        m_transfers->changed().connect([this](const Transfers::ValueType&){
84
 
            update_transfer_connections();
85
 
            update_header();
86
 
            update_section(TRANSFERS);
87
 
            update_section(BULK);
88
 
        });
89
 
 
90
 
        update_transfer_connections();
91
 
    }
92
 
 
93
 
    virtual ~MenuImpl()
94
 
    {
95
 
        g_clear_object(&m_menu);
96
 
    }
97
 
 
98
 
    void update_header()
99
 
    {
100
 
        auto action_group = m_gactions->action_group();
101
 
        auto action_name = g_strdup_printf("%s-header", name());
102
 
        auto state = create_header_state();
103
 
        g_action_group_change_action_state(action_group, action_name, state);
104
 
        g_free(action_name);
105
 
    }
106
 
 
107
 
    std::shared_ptr<Transfers> m_transfers;
108
 
    std::shared_ptr<GActions> m_gactions;
109
 
    GMenu* m_submenu = nullptr;
110
 
 
111
 
private:
112
 
 
113
 
    GVariant* create_header_state()
114
 
    {
115
 
        auto title_v = g_variant_new_string(_("Transfers"));
116
 
        const auto visible = !m_transfers->get().empty();
117
 
 
118
 
        GVariantBuilder b;
119
 
        g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT);
120
 
        g_variant_builder_add(&b, "{sv}", "title", title_v);
121
 
        g_variant_builder_add(&b, "{sv}", "label", title_v);
122
 
        g_variant_builder_add(&b, "{sv}", "accessible-desc", title_v);
123
 
        g_variant_builder_add(&b, "{sv}", "visible", g_variant_new_boolean(visible));
124
 
        return g_variant_builder_end (&b);
125
 
    }
126
 
 
127
 
    void create_gmenu()
128
 
    {
129
 
        g_assert(m_submenu == nullptr);
130
 
 
131
 
        m_submenu = g_menu_new();
132
 
 
133
 
        // build placeholders for the sections
134
 
        for(int i=0; i<NUM_SECTIONS; i++)
135
 
        {
136
 
            auto item = g_menu_item_new(nullptr, nullptr);
137
 
            g_menu_append_item(m_submenu, item);
138
 
            g_object_unref(item);
139
 
        }
140
 
 
141
 
        // add submenu to the header
142
 
        const auto detailed_action = (std::string{"indicator."} + name()) + "-header";
143
 
        auto header = g_menu_item_new(nullptr, detailed_action.c_str());
144
 
        g_menu_item_set_attribute(header, "x-canonical-type", "s",
145
 
                                  "com.canonical.indicator.root");
146
 
        g_menu_item_set_attribute(header, "submenu-action", "s",
147
 
                                  "indicator.calendar-active");
148
 
        g_menu_item_set_submenu(header, G_MENU_MODEL(m_submenu));
149
 
        g_object_unref(m_submenu);
150
 
 
151
 
        // add header to the menu
152
 
        m_menu = g_menu_new();
153
 
        g_menu_append_item(m_menu, header);
154
 
        g_object_unref(header);
155
 
    }
156
 
 
157
 
    GMenuItem* create_transfer_menuitem(const std::shared_ptr<Transfer>& transfer)
158
 
    {
159
 
        const auto& id = transfer->id().c_str();
160
 
        auto menu_item = g_menu_item_new (transfer->id().c_str(), nullptr);
161
 
        g_menu_item_set_attribute (menu_item, "x-canonical-type", "s", "com.canonical.indicator.transfer");
162
 
        g_menu_item_set_attribute (menu_item, "x-canonical-state", "i", transfer->state().get());
163
 
        g_menu_item_set_attribute (menu_item, "x-canonical-uid", "s", id);
164
 
        g_menu_item_set_attribute (menu_item, "x-canonical-time", "x", transfer->last_active().get());
165
 
        g_menu_item_set_action_and_target_value (menu_item, "indicator.activate-transfer", g_variant_new_string(id));
166
 
        return G_MENU_ITEM(menu_item);
167
 
    }
168
 
 
169
 
    void update_transfer(const std::shared_ptr<Transfer>& transfer)
170
 
    {
171
 
        g_message ("FIXME update menu for transfer '%s'", transfer->id().c_str());
172
 
    }
173
 
 
174
 
    GMenuModel* create_transfers_section()
175
 
    {
176
 
        auto menu = g_menu_new();
177
 
 
178
 
        for (const auto& transfer : m_transfers->get())
179
 
        {
180
 
            auto menu_item = create_transfer_menuitem(transfer);
181
 
            g_menu_append_item (menu, menu_item);
182
 
            g_object_unref (menu_item);
183
 
        }
184
 
 
185
 
        return G_MENU_MODEL(menu);
186
 
    }
187
 
 
188
 
    GMenuModel* create_bulk_actions_section()
189
 
    {
190
 
        auto menu = g_menu_new();
191
 
 
192
 
        unsigned int n_running = 0;
193
 
        unsigned int n_stopped = 0;
194
 
        unsigned int n_done = 0;
195
 
        for (const auto& transfer : m_transfers->get()) {
196
 
            switch (transfer->state()) {
197
 
                case Transfer::STARTING:
198
 
                case Transfer::RUNNING: ++n_running; break;
199
 
                case Transfer::FAILED:
200
 
                case Transfer::PAUSED: ++n_stopped; break;
201
 
                case Transfer::CANCELING:
202
 
                case Transfer::DONE: ++n_done; break;
203
 
            }
204
 
        }
205
 
 
206
 
        if (n_stopped)
207
 
            g_menu_append (menu, _("Resume All"), "indicator.resume-all");
208
 
        else if (n_running) 
209
 
            g_menu_append (menu, _("Pause All"), "indicator.pause-all");
210
 
        else if (n_done)
211
 
            g_menu_append (menu, _("Clear All"), "indicator.clear-all");
212
 
 
213
 
        return G_MENU_MODEL(menu);
214
 
    }
215
 
 
216
 
    void update_section(Section section)
217
 
    {
218
 
        GMenuModel * model;
219
 
 
220
 
        switch (section)
221
 
        {
222
 
            case BULK:      model = create_bulk_actions_section(); break;
223
 
            case TRANSFERS: model = create_transfers_section();    break;
224
 
            default:        model = nullptr; g_warn_if_reached();
225
 
        }
226
 
 
227
 
        if (model)
228
 
        {
229
 
            g_menu_remove(m_submenu, section);
230
 
            g_menu_insert_section(m_submenu, section, nullptr, model);
231
 
            g_object_unref(model);
232
 
        }
233
 
    }
234
 
 
235
 
    void update_transfer_connections()
236
 
    {
237
 
        // out with the old
238
 
        for(auto& connection : m_transfer_connections)
239
 
            connection.disconnect();
240
 
        m_transfer_connections.clear();
241
 
 
242
 
        // in with the new
243
 
        for (auto& transfer : m_transfers->get()) {
244
 
            auto connection = transfer->last_active().changed().connect([this,transfer](const time_t&) {
245
 
                update_header();
246
 
                update_transfer(transfer);
247
 
                update_section(BULK);
248
 
            });
249
 
            m_transfer_connections.push_back(connection);
250
 
        }
251
 
    }
252
 
 
253
 
    std::vector<core::Connection> m_transfer_connections;
254
 
 
255
 
}; // class MenuImpl
256
 
 
257
 
/***
258
 
****
259
 
***/
260
 
 
261
 
class PhoneBaseMenu: public MenuImpl
262
 
{
263
 
protected:
264
 
    PhoneBaseMenu(Menu::Profile profile_,
265
 
                  const char* name_,
266
 
                  const std::shared_ptr<Transfers>& transfers_,
267
 
                  const std::shared_ptr<GActions>& gactions_):
268
 
        MenuImpl{profile_, name_, transfers_, gactions_}
269
 
    {
270
 
        update_header();
271
 
    }
272
 
 
273
 
};
274
 
 
275
 
class PhoneMenu: public PhoneBaseMenu
276
 
{
277
 
public:
278
 
    PhoneMenu(const std::shared_ptr<Transfers>& transfers_,
279
 
              const std::shared_ptr<GActions>& actions_):
280
 
        PhoneBaseMenu{PHONE, profile_names[PHONE], transfers_, actions_} {}
281
 
};
282
 
 
283
 
/****
284
 
*****
285
 
****/
286
 
 
287
 
MenuFactory::MenuFactory(const std::shared_ptr<Transfers>& transfers_,
288
 
                         const std::shared_ptr<GActions>& gactions_):
289
 
    m_transfers{transfers_},
290
 
    m_gactions{gactions_}
291
 
{
292
 
}
293
 
 
294
 
std::shared_ptr<Menu>
295
 
MenuFactory::buildMenu(Menu::Profile profile)
296
 
{
297
 
    std::shared_ptr<Menu> menu;
298
 
 
299
 
    switch (profile)
300
 
    {
301
 
    case Menu::PHONE:
302
 
        menu.reset(new PhoneMenu{m_transfers, m_gactions});
303
 
        break;
304
 
 
305
 
    default:
306
 
        g_warn_if_reached();
307
 
        break;
308
 
    }
309
 
    
310
 
    return menu;
311
 
}
312
 
 
313
 
/****
314
 
*****
315
 
****/
316
 
 
317
 
} // namespace transfer
318
 
} // namespace indicator
319
 
} // namespace unity