ed1519708a5f1086f82755fda81a35df84199853
[mevemon] / package / src / ui / fremantle / gui.py
1 #
2 # mEveMon - A character monitor for EVE Online
3 # Copyright (c) 2010  Ryan and Danny Campbell, and the mEveMon Team
4 #
5 # mEveMon is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # mEveMon is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18
19 import sys, time
20
21 import gtk
22 import hildon
23 import gobject
24
25 import glib
26
27 from ui import models
28 import validation
29 import util
30
31 class BaseUI():
32     menu_items = ("Settings", "About", "Refresh")
33
34     def create_menu(self, window):
35         menu = hildon.AppMenu()
36
37         for command in self.menu_items:
38             # Create menu entries
39             button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
40             button.set_label(command)
41
42             if command == "About":
43                 button.connect("clicked", self.about_clicked)
44             elif command == "Settings":
45                 button.connect("clicked", self.settings_clicked, window)
46             elif command == "Refresh":
47                 button.connect("clicked", self.refresh_clicked)
48             else:
49                 assert False, command
50
51             # Add entry to the view menu
52             menu.append(button)
53
54         menu.show_all()
55
56         return menu
57
58     def settings_clicked(self, button, window):
59
60         RESPONSE_NEW, RESPONSE_EDIT, RESPONSE_DELETE = range(3)
61
62         dialog = gtk.Dialog()
63         dialog.set_transient_for(window)
64         dialog.set_title("Settings")
65
66         pannable_area = hildon.PannableArea()
67
68         dialog_vbox = dialog.vbox
69
70         vbox = gtk.VBox(False, 1)
71
72         self.accounts_model = models.AccountsModel(self.controller)
73         
74         accounts_treeview = hildon.GtkTreeView(gtk.HILDON_UI_MODE_NORMAL)
75         accounts_treeview.set_model(self.accounts_model)
76         accounts_treeview.set_headers_visible(True)
77         self.add_columns_to_accounts(accounts_treeview)
78         vbox.pack_start(accounts_treeview, False, False, 1)
79
80         # all stock responses are negative, so we can use any positive value
81         new_button = dialog.add_button("New", RESPONSE_NEW)
82         #TODO: get edit button working
83         #edit_button = dialog.add_button("Edit", RESPONSE_EDIT)
84         delete_button = dialog.add_button("Delete", RESPONSE_DELETE)
85         ok_button = dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
86         
87         pannable_area.add_with_viewport(vbox)
88         
89         dialog_vbox.pack_start(pannable_area, True, True, 1)
90       
91       
92         dialog.show_all()
93
94         result = dialog.run()
95
96         while(result != gtk.RESPONSE_DELETE_EVENT):
97             if result == RESPONSE_NEW:
98                 self.new_account_clicked(window)
99             #elif result == RESPONSE_EDIT:
100             #    # get the selected treeview item and pop up the account_box
101             #    self.edit_account(accounts_treeview)
102             elif result == RESPONSE_DELETE:
103                 # get the selected treeview item, and delete the gconf keys
104                 self.delete_account(accounts_treeview)
105             elif result == gtk.RESPONSE_OK:
106                 self.char_model.get_characters()
107                 break
108         
109             result = dialog.run()
110
111         dialog.destroy()
112
113
114
115     def get_selected_item(self, treeview, column):
116         selection = treeview.get_selection()
117         model, miter = selection.get_selected()
118
119         value = model.get_value(miter, column)
120
121         return value
122
123     def edit_account(self, treeview):
124         uid = self.get_selected_item(treeview, 0)
125         # pop up the account dialog
126
127         self.accounts_model.get_accounts()
128
129     def delete_account(self, treeview):
130         uid = self.get_selected_item(treeview, 0)
131         self.controller.remove_account(uid)
132         # refresh model
133         self.accounts_model.get_accounts()
134
135
136     def add_columns_to_accounts(self, treeview):
137         #Column 0 for the treeview
138         renderer = gtk.CellRendererText()
139         column = gtk.TreeViewColumn('User ID', renderer, 
140                 text=models.AccountsModel.C_UID)
141         column.set_property("expand", True)
142         treeview.append_column(column)
143
144         #Column 2 (characters) for the treeview
145         column = gtk.TreeViewColumn('Characters', renderer, 
146                 markup=models.AccountsModel.C_CHARS)
147         column.set_property("expand", True)
148         treeview.append_column(column)
149
150
151     def new_account_clicked(self, window):
152         dialog = gtk.Dialog()
153     
154         #get the vbox to pack all the settings into
155         vbox = dialog.vbox
156     
157         dialog.set_transient_for(window)
158         dialog.set_title("New Account")
159
160         uidLabel = gtk.Label("User ID:")
161         uidLabel.set_justify(gtk.JUSTIFY_LEFT)
162         vbox.add(uidLabel)
163         
164         uidEntry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
165         uidEntry.set_placeholder("User ID")
166         uidEntry.set_property('is_focus', False)
167         
168         vbox.add(uidEntry)
169
170         apiLabel = gtk.Label("API key:")
171         apiLabel.set_justify(gtk.JUSTIFY_LEFT)
172         vbox.add(apiLabel)
173         
174         apiEntry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
175         apiEntry.set_placeholder("API Key")
176         apiEntry.set_property('is_focus', False)
177
178         vbox.add(apiEntry)
179        
180         ok_button = dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
181
182         dialog.show_all()
183         result = dialog.run()
184         
185         valid_credentials = False
186
187         while not valid_credentials:
188             if result == gtk.RESPONSE_OK:
189                 uid = uidEntry.get_text()
190                 api_key = apiEntry.get_text()
191             
192                 try:
193                     validation.uid(uid)
194                     validation.api_key(api_key)
195                 except validation.ValidationError, e:
196                     self.report_error(e.message)
197                     result = dialog.run()
198                 else:
199                     valid_credentials = True
200                     self.controller.add_account(uid, api_key)
201                     self.accounts_model.get_accounts()
202             else:
203                 break
204
205         dialog.destroy()
206
207
208     def report_error(self, error):
209         hildon.hildon_banner_show_information(self.win, '', error)
210     
211     def about_clicked(self, button):
212         dialog = gtk.AboutDialog()
213         dialog.set_website(self.controller.about_website)
214         dialog.set_website_label(self.controller.about_website)
215         dialog.set_name(self.controller.about_name)
216         dialog.set_authors(self.controller.about_authors)
217         dialog.set_comments(self.controller.about_text)
218         dialog.set_version(self.controller.app_version)
219         dialog.run()
220         dialog.destroy()
221
222     def add_label(self, text, box, markup=True, align="left", padding=1):
223         label = gtk.Label(text)
224         if markup:
225             label.set_use_markup(True)
226         if align == "left":
227             label.set_alignment(0, 0.5)
228
229         box.pack_start(label, False, False, padding)
230
231         return label
232
233 class mEveMonUI(BaseUI):
234
235     def __init__(self, controller):
236         self.controller = controller
237         gtk.set_application_name("mEveMon")
238
239         # create the main window
240         self.win = hildon.StackableWindow()
241         self.win.connect("destroy", self.controller.quit)
242         self.win.show_all()
243         hildon.hildon_gtk_window_set_progress_indicator(self.win, 1)
244
245         # Create menu
246         menu = self.create_menu(self.win)
247         # Attach menu to the window
248         self.win.set_app_menu(menu)
249
250         pannable_area = hildon.PannableArea()
251
252         character_win = CharacterSheetUI(self.controller)
253
254         # gtk.HILDON_UI_MODE_NORMAL -> not selection in the treeview
255         # gtk.HILDON_UI_MODE_EDIT -> selection in the treeview
256         treeview = hildon.GtkTreeView(gtk.HILDON_UI_MODE_NORMAL)
257         treeview.connect('row-activated', character_win.build_window)
258
259         self.char_model = models.CharacterListModel(self.controller)
260         treeview.set_model(self.char_model)
261         self.add_columns_to_treeview(treeview)
262
263         pannable_area.add(treeview)
264
265         self.win.add(pannable_area);
266         
267         self.win.show_all()
268
269         hildon.hildon_gtk_window_set_progress_indicator(self.win, 0)
270
271     def add_columns_to_treeview(self, treeview):
272         #Column 0 for the treeview
273         renderer = gtk.CellRendererPixbuf()
274         column = gtk.TreeViewColumn()
275         column.pack_start(renderer, True)
276         column.add_attribute(renderer, "pixbuf", 
277                 models.CharacterListModel.C_PORTRAIT)
278         treeview.append_column(column)
279
280         #Column 1 for the treeview
281         renderer = gtk.CellRendererText()
282         column = gtk.TreeViewColumn('Character Name', renderer, 
283                 text=models.CharacterListModel.C_NAME)
284         column.set_property("expand", True)
285         treeview.append_column(column)
286  
287     def refresh_clicked(self, button):
288         hildon.hildon_gtk_window_set_progress_indicator(self.win, 1)
289         self.char_model.get_characters()
290         hildon.hildon_gtk_window_set_progress_indicator(self.win, 0)
291     
292
293 class CharacterSheetUI(BaseUI):
294     UPDATE_INTERVAL = 1
295
296     def __init__(self, controller):
297         self.controller = controller
298         self.sheet = None
299         self.char_id = None
300         self.skills_model = None
301
302
303     def build_window(self, treeview, path, view_column):
304         # TODO: this is a really long and ugly function, split it up somehow
305
306         self.win = hildon.StackableWindow()
307         hildon.hildon_gtk_window_set_progress_indicator(self.win, 1)
308         self.win.show_all() 
309
310         # Create menu
311         # NOTE: we probably want a window-specific menu for this page, but the
312         # main appmenu works for now
313         menu = self.create_menu(self.win)
314         # Attach menu to the window
315         self.win.set_app_menu(menu)
316
317         pannable_area = hildon.PannableArea()
318
319         model = treeview.get_model()
320         miter = model.get_iter(path)
321         
322         # column 0 is the portrait, column 1 is name
323         char_name = model.get_value(miter, 1)
324         self.uid = model.get_value(miter, 2)
325         self.char_id = self.controller.char_name2id(char_name)
326
327         self.sheet = self.controller.get_char_sheet(self.uid, self.char_id)
328
329         self.win.set_title(char_name)
330
331
332         hbox = gtk.HBox(False, 0)
333         info_vbox = gtk.VBox(False, 0)
334
335         portrait = gtk.Image()
336         portrait.set_from_file(self.controller.get_portrait(char_name, 256))
337         portrait.show()
338
339         hbox.pack_start(portrait, False, False, 10)
340         hbox.pack_start(info_vbox, False, False, 5)
341
342         vbox = gtk.VBox(False, 0)
343         pannable_area.add_with_viewport(vbox)
344
345         vbox.pack_start(hbox, False, False, 0)
346
347         self.fill_info(info_vbox)
348         self.fill_stats(info_vbox)
349
350         separator = gtk.HSeparator()
351         vbox.pack_start(separator, False, False, 5)
352         separator.show()
353
354         
355         self.add_label("<big>Skill in Training:</big>", vbox, align="normal")
356
357         self.display_skill_in_training(vbox)
358
359         separator = gtk.HSeparator()
360         vbox.pack_start(separator, False, False, 0)
361         separator.show()
362         
363         self.add_label("<big>Skills:</big>", vbox, align="normal")
364
365         skills_treeview = hildon.GtkTreeView(gtk.HILDON_UI_MODE_NORMAL)
366         self.skills_model = models.CharacterSkillsModel(self.controller, self.char_id)
367         skills_treeview.set_model(self.skills_model)
368         self.add_columns_to_skills_view(skills_treeview)
369         vbox.pack_start(skills_treeview, False, False, 1)
370
371         self.win.add(pannable_area)
372         self.win.show_all()
373
374         hildon.hildon_gtk_window_set_progress_indicator(self.win, 0)
375
376     def display_skill_in_training(self, vbox):
377         skill = self.controller.get_skill_in_training(self.uid, self.char_id)
378         
379         if skill.skillInTraining:
380
381             skilltree = self.controller.get_skill_tree()
382             
383             # I'm assuming that we will always find a skill with the skill ID
384             for group in skilltree.skillGroups:
385                 found_skill = group.skills.Get(skill.trainingTypeID, False)
386                 if found_skill:
387                     skill_name = found_skill.typeName
388                     break
389                 
390             self.add_label("%s <small>(Level %d)</small>" % (skill_name, skill.trainingToLevel),
391                     vbox, align="normal")
392             self.add_label("<small>start time: %s\t\tend time: %s</small>" 
393                     %(time.ctime(skill.trainingStartTime),
394                 time.ctime(skill.trainingEndTime)), vbox, align="normal")
395
396             progressbar = gtk.ProgressBar()
397             fraction_completed = (time.time() - skill.trainingStartTime) / \
398                     (skill.trainingEndTime - skill.trainingStartTime)
399
400             progressbar.set_fraction(fraction_completed)
401             align = gtk.Alignment(0.5, 0.5, 0.5, 0)
402             vbox.pack_start(align, False, False, 5)
403             align.show()
404             align.add(progressbar)
405             progressbar.show()
406         else:
407             self.add_label("<small>No skills are currently being trained</small>",
408                     vbox, align="normal")
409
410
411
412     def fill_info(self, box):
413         self.add_label("<big><big>%s</big></big>" % self.sheet.name, box)
414         self.add_label("<small>%s %s %s</small>" % (self.sheet.gender, 
415             self.sheet.race, self.sheet.bloodLine), box)
416         self.add_label("", box, markup=False)
417         self.add_label("<small><b>Corp:</b> %s</small>" % self.sheet.corporationName, box)
418         self.add_label("<small><b>Balance:</b> %s ISK</small>" % 
419                 util.comma(self.sheet.balance), box)
420
421         self.live_sp_val = self.controller.get_sp(self.uid, self.char_id)
422         self.live_sp = self.add_label("<small><b>Total SP:</b> %s</small>" %
423                 util.comma(int(self.live_sp_val)), box)
424         
425         self.spps = self.controller.get_spps(self.uid, self.char_id)[0]
426
427         glib.timeout_add_seconds(self.UPDATE_INTERVAL, self.update_live_sp)
428
429     def fill_stats(self, box):
430
431         atr = self.sheet.attributes
432
433         self.add_label("<small><b>I: </b>%d  <b>M: </b>%d  <b>C: </b>%d  " \
434                 "<b>P: </b>%d  <b>W: </b>%d</small>" % (atr.intelligence,
435                     atr.memory, atr.charisma, atr.perception, atr.willpower), box)
436
437
438     def add_columns_to_skills_view(self, treeview):
439         #Column 0 for the treeview
440         renderer = gtk.CellRendererText()
441         column = gtk.TreeViewColumn('Skill Name', renderer, 
442                 markup=models.CharacterSkillsModel.C_NAME)
443         column.set_property("expand", True)
444         treeview.append_column(column)
445         
446         #Column 1 for the treeview
447         column = gtk.TreeViewColumn('Rank', renderer, 
448                 markup=models.CharacterSkillsModel.C_RANK)
449         column.set_property("expand", True)
450         treeview.append_column(column)
451
452         #Column 2
453         column = gtk.TreeViewColumn('Points', renderer,
454                 markup=models.CharacterSkillsModel.C_SKILLPOINTS)
455         column.set_property("expand", True)
456         treeview.append_column(column)
457
458         #Column 3
459         column = gtk.TreeViewColumn('Level', renderer, 
460                 markup=models.CharacterSkillsModel.C_LEVEL)
461         column.set_property("expand", True)
462         treeview.append_column(column)
463
464
465     def refresh_clicked(self, button):
466         hildon.hildon_gtk_window_set_progress_indicator(self.win, 1)
467         self.skills_model.get_skills()
468         hildon.hildon_gtk_window_set_progress_indicator(self.win, 0)
469
470
471     def update_live_sp(self):
472         # we don't want to keep the timer running in the background
473         # when this callback returns False, the timer destorys itself
474         
475         # TODO: figure out why this doesn't work on the real device
476         #
477         #if not self.win.get_is_topmost():
478         #    return False
479         
480         self.live_sp_val = self.live_sp_val + self.spps * self.UPDATE_INTERVAL
481         self.live_sp.set_label("<small><b>Total SP:</b> %s</small>" %
482                                 util.comma(int(self.live_sp_val)))
483
484         return True
485
486
487 if __name__ == "__main__":
488     main()
489