Initial code
authorJaakko Kyrö <jkyro@korjaussarja.net>
Fri, 7 May 2010 18:03:03 +0000 (21:03 +0300)
committerJaakko Kyrö <jkyro@korjaussarja.net>
Fri, 7 May 2010 18:03:03 +0000 (21:03 +0300)
36 files changed:
.gitignore [new file with mode: 0644]
LICENCE.GPL [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/dirs [new file with mode: 0644]
debian/docs [new file with mode: 0644]
debian/files [new file with mode: 0644]
debian/rules [new file with mode: 0755]
qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.cpp [new file with mode: 0644]
qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.h [new file with mode: 0644]
qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.pri [new file with mode: 0644]
src/eveaccount.cpp [new file with mode: 0644]
src/eveaccount.h [new file with mode: 0644]
src/evehomescreen [new file with mode: 0755]
src/evehomescreen.desktop [new file with mode: 0644]
src/evehomescreen.pro [new file with mode: 0644]
src/evemodel.cpp [new file with mode: 0644]
src/evemodel.h [new file with mode: 0644]
src/evesettingsdialog.cpp [new file with mode: 0644]
src/evesettingsdialog.h [new file with mode: 0644]
src/evesettingsdialog.ui [new file with mode: 0644]
src/eveskilltraining.cpp [new file with mode: 0644]
src/eveskilltraining.h [new file with mode: 0644]
src/main.cpp [new file with mode: 0644]
src/mainwindow.cpp [new file with mode: 0644]
src/mainwindow.h [new file with mode: 0644]
src/mainwindow.ui [new file with mode: 0644]
src/skilltree.cpp [new file with mode: 0644]
src/skilltree.h [new file with mode: 0644]
src/ui_evesettingsdialog.h [new file with mode: 0644]
src/ui_mainwindow.h [new file with mode: 0644]
src/widget.cpp [new file with mode: 0644]
src/widget.h [new file with mode: 0644]
tests/main.cpp [new file with mode: 0644]
tests/tests.pro [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..dc780df
--- /dev/null
@@ -0,0 +1,4 @@
+*~
+*.o
+moc_*
+Makefile
diff --git a/LICENCE.GPL b/LICENCE.GPL
new file mode 100644 (file)
index 0000000..b7b5f53
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..7491f18
--- /dev/null
@@ -0,0 +1,6 @@
+evehomescreen (0.0.1-1) unstable; urgency=low
+
+  * Initial release (Closes: #nnnn)  <nnnn is the bug number of your ITP>
+
+ -- Jaakko Kyro <jkyro@korjaussarja.net>  Wed,  5 May 2010 22:46:36 +0300
+
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..7ed6ff8
--- /dev/null
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..468efd7
--- /dev/null
@@ -0,0 +1,12 @@
+Source: evehomescreen
+Section: user/Network
+Priority: extra
+Maintainer: Jaakko Kyro <jkyro@korjaussarja.net>
+Build-Depends: debhelper (>= 5)
+Standards-Version: 3.7.2
+
+Package: evehomescreen
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}, qt4-maemo5-homescreen-loader
+Description: Homescreen widget to display Eve character training status
+ <insert long description, indented with spaces>
diff --git a/debian/dirs b/debian/dirs
new file mode 100644 (file)
index 0000000..ca882bb
--- /dev/null
@@ -0,0 +1,2 @@
+usr/bin
+usr/sbin
diff --git a/debian/docs b/debian/docs
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/debian/files b/debian/files
new file mode 100644 (file)
index 0000000..06915e6
--- /dev/null
@@ -0,0 +1 @@
+evehomescreen_0.0.1-1_i386.deb user/Network extra
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..3d6a228
--- /dev/null
@@ -0,0 +1,98 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+
+
+
+CFLAGS = -Wall -g
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+       CFLAGS += -O0
+else
+       CFLAGS += -O2
+endif
+
+configure: configure-stamp
+configure-stamp:
+       dh_testdir
+       # Add here commands to configure the package.
+       cd src && /opt/qt4-maemo5/bin/qmake "PREFIX = /usr"
+       touch configure-stamp
+
+
+build: build-stamp
+
+build-stamp: configure-stamp 
+       dh_testdir
+
+       # Add here commands to compile the package.
+       cd src && $(MAKE)
+       #docbook-to-man debian/evehomescreen.sgml > qt-eve-homescreen.1
+
+       touch $@
+
+clean:
+       dh_testdir
+       dh_testroot
+       rm -f build-stamp configure-stamp
+
+       # Add here commands to clean up after the build process.
+       -cd src && $(MAKE) clean
+
+       dh_clean 
+
+install: build
+       dh_testdir
+       dh_testroot
+       dh_clean -k 
+       dh_installdirs
+
+       # Add here commands to install the package into debian/qt-eve-homescreen.
+       cd src && $(MAKE) INSTALL_ROOT=$(CURDIR)/debian/evehomescreen install
+
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+       dh_testdir
+       dh_testroot
+       dh_installchangelogs 
+       dh_installdocs
+       dh_installexamples
+#      dh_install
+#      dh_installmenu
+#      dh_installdebconf       
+#      dh_installlogrotate
+#      dh_installemacsen
+#      dh_installpam
+#      dh_installmime
+#      dh_python
+#      dh_installinit
+#      dh_installcron
+#      dh_installinfo
+       dh_installman
+       dh_link
+       dh_strip
+       dh_compress
+       dh_fixperms
+#      dh_perl
+#      dh_makeshlibs
+       dh_installdeb
+       dh_shlibdeps
+       dh_gencontrol
+       dh_md5sums
+       dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure
diff --git a/qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.cpp b/qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.cpp
new file mode 100644 (file)
index 0000000..b473fea
--- /dev/null
@@ -0,0 +1,287 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmaemo5homescreenadaptor.h"
+
+#include <QtCore/qsocketnotifier.h>
+
+#include <QtGui/qapplication.h>
+#include <QtGui/qx11info_x11.h>
+#include <QtGui/qwidget.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+static QCoreApplication::EventFilter oldEventFilter;
+static QList<QMaemo5HomescreenAdaptor *> allDesktopItems;
+
+static Atom atomByName(const char *name)
+{
+    Atom atom = XInternAtom(QX11Info::display(), name, False);
+    if (!atom)
+        qWarning("Unable to obtain %s atom. This class requires a running Hildon session.", name);
+
+    return atom;
+}
+
+enum HomescreenAtoms
+{
+    HildonAppletId               = 0,
+    NetWmWindowType              = 1,
+    Utf8String                   = 2,
+    HildonTypeHomeApplet         = 3,
+    HildonAppletSettings         = 4,
+    HildonAppletShowSettings     = 5,
+    HildonAppletOnCurrentDesktop = 6,
+    EnumCount                    = 7
+};
+
+static Atom hsAtoms[EnumCount] = { 0, 0, 0, 0, 0, 0, 0 };
+
+static void initAtoms()
+{
+    hsAtoms[HildonAppletId] = atomByName("_HILDON_APPLET_ID");
+    hsAtoms[NetWmWindowType] = atomByName("_NET_WM_WINDOW_TYPE");
+    hsAtoms[Utf8String] = atomByName("UTF8_STRING");
+    hsAtoms[HildonTypeHomeApplet] = atomByName("_HILDON_WM_WINDOW_TYPE_HOME_APPLET");
+    hsAtoms[HildonAppletSettings] = atomByName("_HILDON_APPLET_SETTINGS");
+    hsAtoms[HildonAppletShowSettings] = atomByName("_HILDON_APPLET_SHOW_SETTINGS");
+    hsAtoms[HildonAppletOnCurrentDesktop] = atomByName("_HILDON_APPLET_ON_CURRENT_DESKTOP");
+}
+
+/*! \class QMaemo5HomescreenAdaptor
+
+    \brief The QMaemo5HomescreenAdaptor flags a top-level QWidget as homescreen widget
+
+    QMaemo5HomescreenAdaptor is used in conjunction with the Qt for Maemo homescreen
+    loader. It evaluates the two command line arguments "-plugin-id" and "-write-pipe"
+    to set up a Qt top-level widget as Maemo 5 homescreen widget.
+
+    Note: By default, the widget will have a black background. In order to make the
+    widget transparent, set the Qt::WA_TranslucentBackground widget attribute.
+
+    Example:
+
+    \code
+    QLabel *label = new QLabel("Hello Homescreen");
+    new QMaemo5HomescreenAdaptor(label);
+    label->show();
+    \endcode
+
+    Maemo 5 supports homescreen widgets with settings dialogs. To use it, set
+    the settingsAvailable() property and show a settings dialog when the
+    settingsRequested() signal is emitted.
+
+    Maemo 5 supports more than one homescreen. In order to determine whether
+    the homescreen widget is on the currently visible homescreen, connect to
+    the homescreenChanged() signal.
+*/
+
+/*! \property QMaemo5HomescreenAdaptor::settingsAvailable
+
+    Set this property to true if the widget can make use of a settings dialog,
+    otherwise to false. When this property is set to true, the Maemo 5 homescreen
+    renders a small settings icon on top of the homescreen widget when the
+    user enters the desktop menu. When the user clicks that settings icon, the
+    settingsRequested() signal is emitted.
+
+    The default is false.
+
+    \sa settingsRequested()
+ */
+
+/*! \fn void settingsRequested()
+
+    This signal is emitted every time the homescreen widget's settings icon is
+    invoked by the user. Note that this icon is only visible when the settingsAvailable()
+    property is set.
+
+    \sa settingsAvailable()
+ */
+
+/*! \fn void homescreenChanged(bool isOnCurrentHomescreen)
+
+    This is signal is emitted when current homescreen changes and the homescreen
+    widget becomes visible or invisible. The \a isOnCurrentHomescreen argument
+    indicates whether the homescreen widget is on the current homescreen or not.
+
+    This signal can be used to start/stop background processing in order to save
+    battery life.
+ */
+
+/*!
+    Constructs a new QMaemo5HomescreenAdaptor for the given \a widget.
+
+    Note: The widget must be a top-level widget, and must not be reparented
+    during the lifetime of this adaptor.
+
+    Note: \a widget is also the parent of this class, if the widget is destroyed,
+    so is this adaptor.
+ */
+QMaemo5HomescreenAdaptor::QMaemo5HomescreenAdaptor(QWidget *widget)
+    : QObject(widget),
+      hasSettings(false)
+{
+    Q_ASSERT(widget->isWindow());
+
+    if (!hsAtoms[0])
+        initAtoms();
+
+    Display *display = QX11Info::display();
+
+    const QStringList args = QApplication::arguments();
+
+    // parse the command line arguments.
+    int idx;
+    if ((idx = args.indexOf(QLatin1String("-plugin-id"))) != -1) {
+        appletId = args.value(idx + 1);
+        const QByteArray pluginId = appletId.toUtf8();
+        if (!pluginId.isEmpty()) {
+            XChangeProperty(display,
+                    widget->winId(),
+                    hsAtoms[HildonAppletId],
+                    hsAtoms[Utf8String], 8, PropModeReplace,
+                    reinterpret_cast<const unsigned char *>(pluginId.constData()),
+                    pluginId.length());
+        }
+    }
+    if ((idx = args.indexOf(QLatin1String("-write-pipe"))) != -1) {
+        bool ok;
+        int sockId = args.value(idx + 1).toInt(&ok);
+        if (ok) {
+            socketNotifier = new QSocketNotifier(sockId, QSocketNotifier::Exception, this);
+            connect(socketNotifier, SIGNAL(activated(int)), this, SLOT(socketException()));
+        }
+    }
+
+    // set the X11 atoms to flag our widget as homescreen widget
+    if (!appletId.isEmpty()) {
+        XChangeProperty(display,
+                widget->winId(),
+                hsAtoms[NetWmWindowType],
+                XA_ATOM, 32, PropModeReplace,
+                reinterpret_cast<const unsigned char *>(&hsAtoms[HildonTypeHomeApplet]),
+                1);
+
+        updateStatus();
+
+        // --- make this window a child of root
+        XSetTransientForHint(display, widget->winId(),
+                             RootWindow(display, widget->x11Info().screen()));
+
+        // --- add an x11 event filter
+        if (!oldEventFilter)
+            oldEventFilter = QCoreApplication::instance()->setEventFilter(applicationEventFilter);
+
+        allDesktopItems.append(this);
+    }
+}
+
+QMaemo5HomescreenAdaptor::~QMaemo5HomescreenAdaptor()
+{
+    allDesktopItems.removeOne(this);
+}
+
+/*! \internal */
+void QMaemo5HomescreenAdaptor::updateStatus()
+{
+    if (appletId.isEmpty())
+        return;
+
+    Display *display = QX11Info::display();
+
+    // Set or remove settings property
+    if (hasSettings)
+        XChangeProperty(display,
+                appletWidget()->winId(),
+                hsAtoms[HildonAppletSettings],
+                XA_CARDINAL, 32, PropModeReplace,
+                (const unsigned char *) &(hasSettings), 1);
+    else
+        XDeleteProperty(display,
+                appletWidget()->winId(),
+                hsAtoms[HildonAppletSettings]);
+}
+
+/*! \internal */
+void QMaemo5HomescreenAdaptor::socketException()
+{
+    socketNotifier->setEnabled(false);
+    appletWidget()->close();
+}
+
+/*! \internal */
+bool QMaemo5HomescreenAdaptor::applicationEventFilter(void *message, long *result)
+{
+    bool retval = false;
+
+    if (oldEventFilter)
+        retval = oldEventFilter(message, result);
+
+    if (allDesktopItems.isEmpty())
+        return retval;
+
+    XEvent *ev = reinterpret_cast<XEvent *>(message);
+
+    if (ev->type == ClientMessage) {
+        XClientMessageEvent *cm = (XClientMessageEvent *)message;
+        if (cm->message_type == hsAtoms[HildonAppletShowSettings]) {
+            for (int i = 0; i < allDesktopItems.count(); ++i) {
+                if (allDesktopItems.at(i)->appletWidget()->winId() == ev->xproperty.window) {
+                    emit allDesktopItems.at(i)->settingsRequested();
+                    retval = true;
+                }
+            }
+        }
+    } else if (ev->type == PropertyNotify) {
+        if (ev->xproperty.atom == hsAtoms[HildonAppletOnCurrentDesktop]) {
+            for (int i = 0; i < allDesktopItems.count(); ++i) {
+                if (allDesktopItems.at(i)->appletWidget()->winId() == ev->xproperty.window) {
+                    emit allDesktopItems.at(i)->homescreenChanged(ev->xproperty.window == 0);
+                    retval = true;
+                }
+            }
+        }
+    }
+
+    return retval;
+}
+
diff --git a/qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.h b/qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.h
new file mode 100644 (file)
index 0000000..68c4d5b
--- /dev/null
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMAEMO5HOMESCREENADAPTOR_H
+#define QMAEMO5HOMESCREENADAPTOR_H
+
+#include <QtCore/qobject.h>
+#include <QtGui/qwidget.h>
+
+class QWidget;
+class QSocketNotifier;
+
+class QMaemo5HomescreenAdaptor : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(bool settingsAvailable READ settingsAvailable WRITE setSettingsAvailable)
+
+public:
+    QMaemo5HomescreenAdaptor(QWidget *widget);
+    ~QMaemo5HomescreenAdaptor();
+
+    inline void setSettingsAvailable(bool available)
+    {
+        hasSettings = available;
+        updateStatus();
+    }
+
+    inline bool settingsAvailable() const
+    {
+        return hasSettings;
+    }
+
+Q_SIGNALS:
+    void settingsRequested();
+    void homescreenChanged(bool isOnCurrentHomescreen);
+
+private Q_SLOTS:
+    void socketException();
+
+private:
+    inline QWidget *appletWidget() const { return static_cast<QWidget *>(parent()); }
+
+    void updateStatus();
+
+    static bool applicationEventFilter(void *message, long *result);
+
+    bool hasSettings;
+    QString appletId;
+    QSocketNotifier *socketNotifier;
+};
+
+#endif
diff --git a/qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.pri b/qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.pri
new file mode 100644 (file)
index 0000000..73d41c3
--- /dev/null
@@ -0,0 +1,4 @@
+HEADERS += $$PWD/qmaemo5homescreenadaptor.h
+SOURCES += $$PWD/qmaemo5homescreenadaptor.cpp
+
+INCLUDEPATH += $$PWD
diff --git a/src/eveaccount.cpp b/src/eveaccount.cpp
new file mode 100644 (file)
index 0000000..414999f
--- /dev/null
@@ -0,0 +1,96 @@
+#include "eveaccount.h"
+#include "QXmlStreamReader"
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QtDebug>
+EveCharacter::EveCharacter(QObject *parent) :
+    QObject(parent),
+    characterId(0),
+    corpId(0),
+    characterIcon(NULL)
+{
+}
+
+EveCharacter::EveCharacter(const EveCharacter &aOther):
+        QObject(aOther.parent()),
+        name(aOther.name),
+        corpName(aOther.corpName),
+        characterId(aOther.characterId),
+        corpId(aOther.corpId),
+        characterIcon(NULL)
+{
+    if (aOther.characterIcon) {
+        characterIcon = new QPixmap(*(aOther.characterIcon));
+    }
+}
+
+EveCharacter::~EveCharacter()
+{
+    if (characterIcon)
+        delete characterIcon;
+    characterIcon = NULL;
+}
+
+EveCharacter &EveCharacter::operator =(const EveCharacter &other)
+{
+    name = other.name;
+    characterId = other.characterId;
+    corpId = other.corpId;
+    corpName = other.corpName;
+    qDebug() << "Assignment, characterIcon " << characterIcon;
+    if (characterIcon != NULL) {
+        delete characterIcon;
+        characterIcon = NULL;
+    }
+    qDebug() << " after deletion";
+    qDebug() << " other icon" << other.characterIcon;
+    if (other.characterIcon != NULL)
+        characterIcon = new QPixmap(*(other.characterIcon));
+    qDebug() << "Assignment done";
+    return *this;
+}
+
+bool EveCharacter::fromXml(QXmlStreamReader &xml)
+{
+    if (xml.name() != "row")
+        return false;
+    name = xml.attributes().value("","name").toString();
+    characterId = xml.attributes().value("","characterID").toString().toInt();
+    corpName = xml.attributes().value("","corporationName").toString();
+    corpId = xml.attributes().value("","corporationID").toString().toInt();
+    return true;
+}
+
+bool EveCharacter::fetchImage()
+{
+    if (characterId == 0) {
+        qDebug() << "No character";
+        return false;
+    }
+    if (characterIcon != NULL) {
+        return true;
+
+    }
+    qDebug() << "Requesting image";
+    QNetworkRequest req(QUrl(QString("http://img.eve.is/serv.asp?c=%1&s=64").arg(characterId)));
+    m_reply = mgr.get(req);
+    connect(m_reply,SIGNAL(finished()),this,SLOT(imageReady()));
+    return true;
+}
+
+// Construct the pixmap from the reply data
+// and delete the reply
+void EveCharacter::imageReady()
+{
+    qDebug() << "Image ready";
+    characterIcon = new QPixmap;
+    if (!characterIcon->loadFromData(m_reply->readAll())) {
+        delete characterIcon;
+        characterIcon = NULL;
+        qDebug() << "Cannot load image";
+        return;
+    }
+    m_reply->deleteLater();
+    emit imageLoaded();
+}
diff --git a/src/eveaccount.h b/src/eveaccount.h
new file mode 100644 (file)
index 0000000..1e1ec38
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef EVEACCOUNT_H
+#define EVEACCOUNT_H
+
+#include <QObject>
+#include <QIODevice>
+#include <QXmlStreamReader>
+#include <QPixmap>
+#include <QNetworkReply>
+#include <QNetworkConfigurationManager>
+
+#include "eveskilltraining.h"
+class EveCharacter : public QObject
+{
+    Q_OBJECT
+public:
+    explicit EveCharacter(QObject *parent = 0);
+    EveCharacter(const EveCharacter &aOther);
+    EveCharacter &operator=(const EveCharacter&other);
+    ~EveCharacter();
+    bool fromXml(QXmlStreamReader &xml);
+    bool fetchImage();
+signals:
+    void imageLoaded();
+public slots:
+    void imageReady();
+public:
+    QString name;
+    QString corpName;
+    int characterId;
+    int corpId;
+    QPixmap *characterIcon;
+    EveSkillTraining currentSkill;
+private:
+    QNetworkReply *m_reply;
+    QNetworkAccessManager mgr;
+};
+
+#endif // EVEACCOUNT_H
diff --git a/src/evehomescreen b/src/evehomescreen
new file mode 100755 (executable)
index 0000000..92960a9
Binary files /dev/null and b/src/evehomescreen differ
diff --git a/src/evehomescreen.desktop b/src/evehomescreen.desktop
new file mode 100644 (file)
index 0000000..4e663ff
--- /dev/null
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Name=EVE Homescreen widget
+Comment=See character training
+Type=qt
+X-Path=evehomescreen
+X-Multiple-Instances=false
+X-home-applet-minwidth=128
+X-home-applet-minheight=128
+Name[en_US]=EVE Homescreen widget
+
diff --git a/src/evehomescreen.pro b/src/evehomescreen.pro
new file mode 100644 (file)
index 0000000..6969bdb
--- /dev/null
@@ -0,0 +1,64 @@
+# -------------------------------------------------
+# Project created by QtCreator 2010-03-24T21:05:00
+# -------------------------------------------------
+QT += core \
+    gui \
+    network \
+    maemo5
+TARGET = evehomescreen
+TEMPLATE = app
+SOURCES += main.cpp \
+    widget.cpp \
+    evemodel.cpp \
+    eveaccount.cpp \
+    eveskilltraining.cpp \
+    evesettingsdialog.cpp \
+    mainwindow.cpp \
+    skilltree.cpp
+HEADERS += widget.h \
+    evemodel.h \
+    eveaccount.h \
+    eveskilltraining.h \
+    evesettingsdialog.h \
+    mainwindow.h \
+    skilltree.h
+FORMS += evesettingsdialog.ui \
+    mainwindow.ui
+
+CONFIG += mobility
+MOBILITY += bearer
+
+include(../qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.pri)
+
+unix {
+    #VARIABLES
+    isEmpty(PREFIX) {
+        PREFIX = /usr/local
+  }
+BINDIR = $$PREFIX/bin
+DATADIR =$$PREFIX/share
+
+DEFINES += DATADIR=\\\"$$DATADIR\\\" PKGDATADIR=\\\"$$PKGDATADIR\\\"
+
+#MAKE INSTALL
+
+INSTALLS += target desktop iconxpm icon26 icon48 icon64
+
+  target.path =$$PREFIX/lib/hildon-desktop
+
+  desktop.path = $$DATADIR/applications/hildon-home
+  desktop.files += $${TARGET}.desktop
+
+  iconxpm.path = $$DATADIR/pixmap
+  iconxpm.files += ../data/maemo/$${TARGET}.xpm
+
+  icon26.path = $$DATADIR/icons/hicolor/26x26/apps
+  icon26.files += ../data/26x26/$${TARGET}.png
+
+  icon48.path = $$DATADIR/icons/hicolor/48x48/apps
+  icon48.files += ../data/48x48/$${TARGET}.png
+
+  icon64.path = $$DATADIR/icons/hicolor/64x64/apps
+  icon64.files += ../data/64x64/$${TARGET}.png
+}
+
diff --git a/src/evemodel.cpp b/src/evemodel.cpp
new file mode 100644 (file)
index 0000000..9b44c7c
--- /dev/null
@@ -0,0 +1,87 @@
+#include "evemodel.h"
+#include "eveaccount.h"
+#include <QNetworkAccessManager>
+#include <QXmlStreamReader>
+#include <QUrl>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QtDebug>
+static QUrl imageUrl("http://img.eve.is/serv.asp");
+
+EveModel::EveModel(QObject *parent) :
+    QObject(parent),
+    m_userId(0)
+{
+}
+
+void EveModel::fetchAccounts()
+{
+    QNetworkRequest fetch(QUrl(QString("http://api.eveonline.com/account/Characters.xml.aspx?apiKey=%1&userID=%2")
+                               .arg(apiKey())
+                               .arg(userId())));
+    m_reply = mgr.get(fetch);
+    qDebug() << "Fetch accounts, reply " << m_reply;
+    connect(m_reply,SIGNAL(finished()),this,SLOT(replyReady()));
+    connect(m_reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(networkError(QNetworkReply::NetworkError)));
+}
+
+void EveModel::replyReady()
+{
+    if (m_reply->error()) {
+        qDebug() << "Failed! " << m_reply->errorString();
+        return;
+    }
+    QByteArray reply = m_reply->readAll();
+    qDebug() << "Reply ready";
+    qDebug() << reply;
+    QXmlStreamReader reader(reply);
+
+    reader.readNextStartElement();
+    if (reader.error()) {
+        qDebug() << "Parse error: " << reader.errorString();
+    }
+    qDebug() << "Relement: " << reader.name();
+    if (reader.name() != "eveapi")
+        return;
+    reader.readNextStartElement();
+    if (reader.error()) {
+        qDebug() << "Parse error: " << reader.errorString();
+    }
+    qDebug() << "1 element: " << reader.name();
+    if (reader.name() != "currentTime")
+        return;
+    reader.readNextStartElement();
+    reader.readNextStartElement();
+    if (reader.error()) {
+        qDebug() << "Parse error: " << reader.errorString();
+    }
+    qDebug() << "2 element: " << reader.name();
+    if (reader.name() != "result")
+        return;
+    reader.readNextStartElement();
+    qDebug() << "Relement: " << reader.name();
+    if (reader.name() != "rowset")
+        return;
+    reader.readNextStartElement();
+    qDebug() << "3 element: " << reader.name();
+    if (reader.name() == "row")
+        m_characters.clear();
+    while (reader.name() == "row") {
+        EveCharacter chara;
+        if (!chara.fromXml(reader)) {
+            qDebug() << "Error parsing character data";
+            break;
+        }
+        m_characters.append(chara);
+        reader.readNextStartElement(); // end current row
+        reader.readNextStartElement(); // start next row
+    }
+    m_reply->deleteLater();
+    emit accountsReady();
+}
+
+void EveModel::networkError(QNetworkReply::NetworkError error)
+{
+    qDebug() << "Network error " << error;
+}
+
diff --git a/src/evemodel.h b/src/evemodel.h
new file mode 100644 (file)
index 0000000..1d09ce1
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef EVEMODEL_H
+#define EVEMODEL_H
+
+#include "eveaccount.h"
+#include <QObject>
+#include <QString>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+class EveModel : public QObject
+{
+    Q_OBJECT
+public:
+    explicit EveModel(QObject *parent = 0);
+    void setApiKey(QString aKey) { m_apiKey = aKey; }
+    QString apiKey() const { return m_apiKey; }
+
+    void setUserId(int aId) { m_userId = aId; }
+    int userId() const { return m_userId; }
+    QList<EveCharacter> &characters() { return m_characters; }
+signals:
+    void accountsReady();
+
+public slots:
+    void fetchAccounts();
+private slots:
+    void replyReady();
+    void networkError(QNetworkReply::NetworkError);
+private:
+    QString m_apiKey;
+    int m_userId;
+    QNetworkAccessManager mgr;
+    QNetworkReply *m_reply;
+    QList<EveCharacter> m_characters;
+};
+
+#endif // EVEMODEL_H
diff --git a/src/evesettingsdialog.cpp b/src/evesettingsdialog.cpp
new file mode 100644 (file)
index 0000000..0352654
--- /dev/null
@@ -0,0 +1,127 @@
+#include "evesettingsdialog.h"
+#include "ui_evesettingsdialog.h"
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QNetworkRequest>
+#include <QtDebug>
+#include <QtMaemo5>
+#include <QtGui>
+#include <QSettings>
+
+EveSettingsDialog::EveSettingsDialog(QWidget *parent) :
+    QDialog(parent),
+    m_reply(0),
+    m_selection(0),
+    userIdEdit(new QLineEdit(this)),
+    apiKeyEdit(new QLineEdit(this)),
+    characterPicker(new QMaemo5ValueButton(this)),
+    characterSelector(new QMaemo5ListPickSelector),
+    characterModel(new QStandardItemModel(0,1,this)),
+    m_layout(new QVBoxLayout(this))
+{
+    setWindowTitle("EVE account");
+    //QHBoxLayout *hbox = new QHBoxLayout(this);
+    //QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel,Qt::Vertical,this);
+    //hbox->addLayout(m_layout);
+    //hbox->addWidget(buttons);
+    setLayout(m_layout);
+    QLabel *label1 = new QLabel("User ID");
+    m_layout->addWidget(label1);
+    m_layout->addWidget(userIdEdit);
+    QLabel *label2 = new QLabel("API key");
+    m_layout->addWidget(label2);
+    m_layout->addWidget(apiKeyEdit);
+    m_layout->addWidget(characterPicker);
+    characterSelector->setModel(characterModel);
+    characterSelector->setModelColumn(0);
+    characterPicker->setValueLayout(QMaemo5ValueButton::ValueBesideText);
+    characterPicker->setText("Character");
+
+    QPushButton *button = new QPushButton("Done");
+    m_layout->addWidget(button);
+    characterPicker->setPickSelector(characterSelector);
+
+    connect(button,SIGNAL(pressed()),this,SLOT(accept()));
+    //connect(buttons,SIGNAL(rejected()),this,SLOT(reject()));
+    //connect(ui->getCharactersButton,SIGNAL(pressed()),this,SLOT(tryLoadCharacters()));
+    //connect(ui->characterCombo,SIGNAL(currentIndexChanged(int)),this,SLOT(characterSelected(int)));
+    //connect(apiKeyEdit,SIGNAL(editingFinished()),this,SLOT(tryLoadCharacters()));
+    //connect(userIdEdit,SIGNAL(editingFinished()),this,SLOT(tryLoadCharacters()));
+    //connect(characterSelector->view(),SIGNAL(activated(const QModelIndex &)),this,SLOT(characterSelected()));
+    connect(characterPicker,SIGNAL(pressed()),this,SLOT(tryLoadCharacters()));
+    connect(characterSelector,SIGNAL(selected(const QString &)),characterPicker,SLOT(setValuetext(const QString &)));
+}
+
+void EveSettingsDialog::changeEvent(QEvent *e)
+{
+    QDialog::changeEvent(e);
+    switch (e->type()) {
+    case QEvent::LanguageChange:
+        //retranslateUi(this);
+        break;
+    default:
+        break;
+    }
+}
+
+void EveSettingsDialog::tryLoadCharacters()
+{
+    if (!apiKeyEdit->text().isEmpty() &&
+        !userIdEdit->text().isEmpty()) {
+        qDebug() << "Load characters, API key " << apiKeyEdit->text() << " user ID: " << userIdEdit->text();
+        characterModel->clear();
+        QStandardItem *item = new QStandardItem("Retrieving characters, please wait...");
+
+        characterModel->appendRow(item);
+        if (m_model.isNull()) {
+            m_model = QSharedPointer<EveModel>(new EveModel);
+        }
+        m_model->setApiKey(apiKeyEdit->text());
+        m_model->setUserId(userIdEdit->text().toInt());
+        connect(m_model.data(),SIGNAL(accountsReady()),this,SLOT(charactersLoaded()));
+        m_model->fetchAccounts();
+        setAttribute(Qt::WA_Maemo5ShowProgressIndicator, true);
+    }
+}
+
+// Setting the model also sets the
+// API key and user info since
+// Widget is responsible for loading and saving
+
+void EveSettingsDialog::setModel(QSharedPointer<EveModel> &model)
+{
+    m_model = model;
+    apiKeyEdit->setText(model->apiKey());
+    userIdEdit->setText(QString::number(model->userId()));
+    charactersLoaded();
+}
+
+void EveSettingsDialog::charactersLoaded()
+{
+    setAttribute(Qt::WA_Maemo5ShowProgressIndicator, false);
+    EveCharacter c;
+    qDebug() << "Characters loaded";
+    characterModel->clear();
+    foreach (c,m_model->characters()) {
+        qDebug() << "Found " << c.name;
+        QStandardItem *item = new QStandardItem(c.name);
+        item->setTextAlignment(Qt::AlignCenter);
+        item->setEditable(false);
+        characterModel->appendRow(item);
+    }
+}
+
+EveCharacter *EveSettingsDialog::selectedCharacter() {
+    int idx = characterSelector->currentIndex();
+    qDebug() << "Selected index is " << idx;
+    if ( idx >=0 && idx < m_model->characters().size() ) {
+        qDebug() << "Selecting " << m_model->characters().at(idx).name;
+        return &(m_model->characters()[idx]);
+    }
+    return NULL;
+}
+
+void EveSettingsDialog::characterSelected()
+{
+
+}
diff --git a/src/evesettingsdialog.h b/src/evesettingsdialog.h
new file mode 100644 (file)
index 0000000..47832c5
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef EVESETTINGSDIALOG_H
+#define EVESETTINGSDIALOG_H
+
+#include <QDialog>
+#include "evemodel.h"
+#include <QtMaemo5>
+#include <QtGui>
+#include <QSharedPointer>
+
+class QNetworkReply;
+class EveCharacter;
+namespace Ui {
+    class EveSettingsDialog;
+}
+
+class EveSettingsDialog : public QDialog {
+    Q_OBJECT
+public:
+    EveSettingsDialog(QWidget *parent = 0);
+
+    EveCharacter *selectedCharacter();
+    QSharedPointer<EveModel> model() { return m_model; }
+    void setModel(QSharedPointer<EveModel> &model);
+protected:
+    void changeEvent(QEvent *e);
+public slots:
+    void tryLoadCharacters();
+    void charactersLoaded();
+    void characterSelected();
+private:
+    QSharedPointer<EveModel> m_model;
+    QNetworkReply *m_reply;
+    EveCharacter *m_selection;
+    QLineEdit *userIdEdit;
+    QLineEdit *apiKeyEdit;
+    QMaemo5ValueButton *characterPicker;
+    QMaemo5ListPickSelector *characterSelector;
+    QStandardItemModel *characterModel;
+    QVBoxLayout *m_layout;
+
+};
+
+#endif // EVESETTINGSDIALOG_H
diff --git a/src/evesettingsdialog.ui b/src/evesettingsdialog.ui
new file mode 100644 (file)
index 0000000..4637455
--- /dev/null
@@ -0,0 +1,221 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>EveSettingsDialog</class>
+ <widget class="QDialog" name="EveSettingsDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>800</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>EVE settings</string>
+  </property>
+  <widget class="QDialogButtonBox" name="buttonBox">
+   <property name="geometry">
+    <rect>
+     <x>710</x>
+     <y>20</y>
+     <width>81</width>
+     <height>241</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Vertical</enum>
+   </property>
+   <property name="standardButtons">
+    <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_4">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>10</y>
+     <width>171</width>
+     <height>34</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'DejaVu Sans'; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;table style=&quot;-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;&quot;&gt;
+&lt;tr&gt;
+&lt;td style=&quot;border: none;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;To see character info visit&lt;br /&gt;&lt;a href=&quot;http://www.eveonline.com/api/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;EVE Online account page&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/body&gt;&lt;/html&gt;</string>
+   </property>
+   <property name="openExternalLinks">
+    <bool>true</bool>
+   </property>
+  </widget>
+  <widget class="QScrollArea" name="scrollArea">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>50</y>
+     <width>701</width>
+     <height>241</height>
+    </rect>
+   </property>
+   <property name="widgetResizable">
+    <bool>true</bool>
+   </property>
+   <widget class="QWidget" name="scrollAreaWidgetContents">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>0</y>
+      <width>695</width>
+      <height>235</height>
+     </rect>
+    </property>
+    <widget class="QLineEdit" name="apiKeyEdit">
+     <property name="geometry">
+      <rect>
+       <x>10</x>
+       <y>10</y>
+       <width>671</width>
+       <height>61</height>
+      </rect>
+     </property>
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="font">
+      <font>
+       <pointsize>24</pointsize>
+      </font>
+     </property>
+     <property name="frame">
+      <bool>true</bool>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>0</y>
+       <width>39</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>API key</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_2">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>70</y>
+       <width>34</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>UserID</string>
+     </property>
+    </widget>
+    <widget class="QLineEdit" name="userIdEdit">
+     <property name="geometry">
+      <rect>
+       <x>10</x>
+       <y>90</y>
+       <width>661</width>
+       <height>61</height>
+      </rect>
+     </property>
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="font">
+      <font>
+       <pointsize>24</pointsize>
+      </font>
+     </property>
+    </widget>
+    <widget class="QComboBox" name="characterCombo">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>170</y>
+       <width>70</width>
+       <height>24</height>
+      </rect>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_3">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>153</y>
+       <width>87</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>Choose character</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="getCharactersButton">
+     <property name="geometry">
+      <rect>
+       <x>540</x>
+       <y>180</y>
+       <width>121</width>
+       <height>22</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>Fetch characters</string>
+     </property>
+    </widget>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>EveSettingsDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>EveSettingsDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/src/eveskilltraining.cpp b/src/eveskilltraining.cpp
new file mode 100644 (file)
index 0000000..4ee0c31
--- /dev/null
@@ -0,0 +1,98 @@
+#include "eveskilltraining.h"
+#include "evemodel.h"
+#include "eveaccount.h"
+
+#include <QXmlStreamReader>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QtDebug>
+EveSkillTraining::EveSkillTraining(QObject *parent) :
+    QObject(parent),
+    training(false),
+    typeId(0),
+    startSkillpoints(0),
+    destSkillpoints(0),
+    level(0),
+    m_character(NULL),
+    m_account(NULL),
+    m_reply(NULL)
+{
+}
+
+bool EveSkillTraining::fromXml(QXmlStreamReader &xml)
+{
+    if (xml.name() != "result") {
+        qDebug() << "Wrong element:" << xml.name();
+        return false;
+    }
+    qDebug() << "passed, element is result";
+    bool inResult = true;
+    while (inResult) {
+        inResult = xml.readNextStartElement();
+        qDebug() << "element:" << xml.name();
+        if (xml.name() == "trainingEndTime") {
+            qDebug() << "Parse end time";
+            endTime = QDateTime::fromString(xml.readElementText(),"yyyy-MM-dd hh:mm:ss");//2008-08-17 06:43:00
+            qDebug()<< endTime;
+
+        } else if (xml.name() == "trainingStartTime") {
+            qDebug() << "Parse start time";
+            startTime = QDateTime::fromString(xml.readElementText(),"yyyy-MM-dd hh:mm:ss");//2008-08-17 06:43:00
+            qDebug() << startTime;
+
+        } else if (xml.name() == "trainingTypeID") {
+            typeId = xml.readElementText().toInt();
+
+        } else if (xml.name() == "trainingStartSP"){
+            startSkillpoints = xml.readElementText().toInt();
+
+        } else if (xml.name() == "trainingDestinationSP"){
+            destSkillpoints = xml.readElementText().toInt();
+
+        } else if (xml.name() == "trainingToLevel"){
+            level = xml.readElementText().toInt();
+        } else
+            // Noop
+            xml.skipCurrentElement();
+
+    } // while
+    qDebug() << "Parsing finished";
+    return true;
+}
+
+void EveSkillTraining::fetchInfo()
+{
+    if (m_character == NULL || m_account == NULL)
+        return;
+    QNetworkRequest req(QUrl(QString("http://api.eveonline.com/char/SkillInTraining.xml.aspx?apiKey=%1&userID=%2&characterID=%3")
+                             .arg(m_account->apiKey())
+                             .arg(m_account->userId())
+                             .arg(m_character->characterId)));
+    m_reply = m_mgr.get(req);
+    connect(m_reply,SIGNAL(finished()),this,SLOT(infoReady()));
+}
+
+void EveSkillTraining::infoReady()
+{
+    if (m_reply->error()) {
+        qDebug() << "Failed! " << m_reply->errorString();
+        return;
+    }
+    qDebug() << "Skill reply";
+    QByteArray reply = m_reply->readAll();
+    qDebug() << "Reply ready";
+    qDebug() << reply;
+    QXmlStreamReader reader(reply);
+
+    reader.readNextStartElement();
+    if (reader.name() != "eveapi")
+        return;
+    reader.readNextStartElement();
+    if (reader.name() != "currentTime")
+        return;
+    reader.readNextStartElement(); // end currentTime element
+    reader.readNextStartElement(); // start result element
+    fromXml(reader);
+    emit finished();
+}
diff --git a/src/eveskilltraining.h b/src/eveskilltraining.h
new file mode 100644 (file)
index 0000000..1ddc9ce
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef EVESKILLTRAINING_H
+#define EVESKILLTRAINING_H
+
+#include <QObject>
+#include <QDateTime>
+#include <QString>
+#include <QXmlStreamReader>
+#include <QNetworkAccessManager>
+
+class EveCharacter;
+class EveModel;
+
+class EveSkillTraining : public QObject
+{
+    Q_OBJECT
+public:
+    explicit EveSkillTraining(QObject *parent = 0);
+    bool fromXml(QXmlStreamReader &xml);
+    void setCharacter(EveCharacter *character) { m_character = character; }
+    void setAccount(EveModel *aModel) { m_account = aModel; }
+signals:
+    void finished();
+
+public slots:
+    void fetchInfo();
+    void infoReady();
+
+public:
+    bool training;
+    QDateTime startTime;
+    QDateTime endTime;
+    int typeId;
+    int startSkillpoints;
+    int destSkillpoints;
+    int level;
+private:
+    EveCharacter *m_character;
+    EveModel *m_account;
+    QNetworkAccessManager m_mgr;
+    QNetworkReply *m_reply;
+};
+
+#endif // EVESKILLTRAINING_H
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644 (file)
index 0000000..6ab2198
--- /dev/null
@@ -0,0 +1,21 @@
+#include <QtGui/QApplication>
+#include "widget.h"
+#include "qmaemo5homescreenadaptor.h"
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+    QApplication::setGraphicsSystem("native");
+    QApplication a(argc, argv);
+    //MainWindow w;
+    Widget w;
+    QMaemo5HomescreenAdaptor *adaptor = new QMaemo5HomescreenAdaptor(&w);
+    adaptor->setSettingsAvailable(true);
+    QObject::connect(adaptor,SIGNAL(settingsRequested()),&w,SLOT(showSettingsDialog()));
+#if defined(Q_WS_S60)
+    w.showMaximized();
+#else
+    w.show();
+#endif
+    return a.exec();
+}
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
new file mode 100644 (file)
index 0000000..0e09abb
--- /dev/null
@@ -0,0 +1,59 @@
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+#include "evesettingsdialog.h"
+#include <QtDebug>
+MainWindow::MainWindow(QWidget *parent) :
+    QMainWindow(parent),
+    ui(new Ui::MainWindow),
+    m_settings(new EveSettingsDialog(this))
+{
+    ui->setupUi(this);
+    connect(ui->fetchButton,SIGNAL(pressed()),this,SLOT(settingsRequested()));
+}
+
+MainWindow::~MainWindow()
+{
+    delete ui;
+}
+
+void MainWindow::changeEvent(QEvent *e)
+{
+    QMainWindow::changeEvent(e);
+    switch (e->type()) {
+    case QEvent::LanguageChange:
+        ui->retranslateUi(this);
+        break;
+    default:
+        break;
+    }
+}
+
+void MainWindow::settingsRequested()
+{
+    int result = m_settings->exec();
+    if (result == QDialog::Accepted) {
+        m_character = m_settings->selectedCharacter();
+
+        if (m_character != NULL ) {
+            qDebug() << "Selected character:" << m_character->name;
+            connect(m_character,SIGNAL(imageLoaded()),this,SLOT(setLabelImage()));
+            m_character->fetchImage();
+
+            skill.setCharacter(m_character);
+            skill.setAccount(m_settings->model().data());
+            connect(&skill, SIGNAL(finished()),this,SLOT(setSkillTraining()));
+            skill.fetchInfo();
+        }
+    }
+}
+
+void MainWindow::setLabelImage()
+{
+    ui->iconLabel->setPixmap(*(m_character->characterIcon));
+}
+
+void MainWindow::setSkillTraining()
+{
+    ui->trainingLabel->setText(QString("Skill ready at %1").arg(skill.endTime.toString(Qt::SystemLocaleShortDate)));
+}
+
diff --git a/src/mainwindow.h b/src/mainwindow.h
new file mode 100644 (file)
index 0000000..23f9170
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include "evesettingsdialog.h"
+#include "eveaccount.h"
+
+namespace Ui {
+    class MainWindow;
+}
+
+class MainWindow : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    explicit MainWindow(QWidget *parent = 0);
+    ~MainWindow();
+public slots:
+    void settingsRequested();
+    void setLabelImage();
+    void setSkillTraining();
+protected:
+    void changeEvent(QEvent *e);
+
+private:
+    Ui::MainWindow *ui;
+    EveSettingsDialog *m_settings;
+    EveCharacter *m_character;
+    EveSkillTraining skill;
+};
+
+#endif // MAINWINDOW_H
diff --git a/src/mainwindow.ui b/src/mainwindow.ui
new file mode 100644 (file)
index 0000000..e738d8e
--- /dev/null
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>394</width>
+    <height>236</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <widget class="QPushButton" name="fetchButton">
+    <property name="geometry">
+     <rect>
+      <x>280</x>
+      <y>150</y>
+      <width>90</width>
+      <height>22</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Fetch!</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="iconLabel">
+    <property name="geometry">
+     <rect>
+      <x>80</x>
+      <y>60</y>
+      <width>70</width>
+      <height>70</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>TextLabel</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="trainingLabel">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>150</y>
+      <width>221</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+    <property name="alignment">
+     <set>Qt::AlignCenter</set>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>394</width>
+     <height>20</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/skilltree.cpp b/src/skilltree.cpp
new file mode 100644 (file)
index 0000000..0bdfbe4
--- /dev/null
@@ -0,0 +1,109 @@
+#include "skilltree.h"
+#include <QFile>
+#include <QDataStream>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QNetworkRequest>
+#include <QtDebug>
+#include <QXmlStreamReader>
+#include <assert.h>
+
+SkillTree::SkillTree(QObject *parent) :
+    QObject(parent),
+    mgr(0),
+    reply(0)
+{
+    QFile cacheFile("/var/tmp/skillCache");
+    if (cacheFile.exists()) {
+        QDataStream input(&cacheFile);
+        input >> skillNames;
+    } else {
+        // load from network
+
+    }
+
+}
+
+void SkillTree::loadSkills()
+{
+    QFile cacheFile("/var/tmp/skillCache");
+    if (cacheFile.exists()) {
+        cacheFile.open(QIODevice::ReadOnly);
+        qDebug() << "Load from file";
+        QDataStream input(&cacheFile);
+        input >> skillNames;
+        emit skillsLoaded();
+    } else {
+        // load from network
+        qDebug() << "load skills from network";
+        mgr = new QNetworkAccessManager(this);
+        QNetworkRequest req(QUrl("http://api.eveonline.com//eve/SkillTree.xml.aspx"));
+        reply = mgr->get(req);
+        connect(reply,SIGNAL(finished()),this,SLOT(replyReady()));
+    }
+}
+
+void SkillTree::replyReady()
+{
+    qDebug() << "Reply ready";
+    if (!reply->error()) {
+        QByteArray replyContent = reply->readAll();
+        qDebug() << replyContent;
+        fromXml(replyContent);
+        emit skillsLoaded();
+    } else {
+        qDebug() << "fetch error:" << reply->errorString();
+    }
+}
+
+void SkillTree::fromXml(QByteArray &content)
+{
+    QXmlStreamReader xml(content);
+    xml.readNextStartElement(); //eveapi
+    //assert(xml.name() == "eveapi");
+    xml.readNextStartElement(); //currentTime
+    xml.skipCurrentElement();
+    //assert(xml.name() == "currentTime");
+    xml.readNextStartElement(); //result
+    xml.readNextStartElement(); // rowset for skill groups
+    bool groupsLeft = xml.readNextStartElement(); // row for skill group
+    while (groupsLeft) {
+        xml.readNextStartElement(); // rowset for skills
+        bool skillsLeft = xml.readNextStartElement(); // row for skill
+        while (skillsLeft) {
+            assert(xml.name() == "row");
+            QString name = xml.attributes().value("","typeName").toString();
+            int typeId = xml.attributes().value("","typeID").toString().toInt();
+            qDebug() << "Found skill " << name << ":" << typeId;
+            skillNames[typeId] = name;
+
+            xml.skipCurrentElement(); // description
+            skillsLeft = xml.readNextStartElement(); // next skill row
+        }
+        xml.readNextStartElement(); // rowset ends
+        groupsLeft = xml.readNextStartElement(); // next skill group row
+    }
+    int len = 0;
+    QString name;
+    QString longestName;
+    foreach (name,skillNames) {
+        if (name.length() > len) {
+            longestName = name;
+            len = name.length();
+        }
+    }
+
+    qDebug() << skillNames;
+    qDebug() << longestName;
+}
+
+void SkillTree::save()
+{
+    QFile cache("/var/tmp/skillCache");
+    cache.open(QIODevice::WriteOnly);
+    QDataStream output(&cache);
+    output << skillNames;
+    cache.flush();
+    cache.close();
+}
+
diff --git a/src/skilltree.h b/src/skilltree.h
new file mode 100644 (file)
index 0000000..d39710e
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef SKILLTREE_H
+#define SKILLTREE_H
+
+#include <QObject>
+#include <QHash>
+
+class QNetworkAccessManager;
+class QNetworkReply;
+
+class SkillTree : public QObject
+{
+    Q_OBJECT
+public:
+    explicit SkillTree(QObject *parent = 0);
+    QString skillName(const int code) const { return skillNames.value(code); }
+signals:
+    void skillsLoaded();
+public slots:
+    void loadSkills();
+    void replyReady();
+    void save();
+private:
+    void fromXml(QByteArray &content);
+    QHash<int,QString> skillNames;
+    QNetworkAccessManager *mgr;
+    QNetworkReply *reply;
+};
+
+#endif // SKILLTREE_H
diff --git a/src/ui_evesettingsdialog.h b/src/ui_evesettingsdialog.h
new file mode 100644 (file)
index 0000000..aa555a4
--- /dev/null
@@ -0,0 +1,132 @@
+/********************************************************************************
+** Form generated from reading UI file 'evesettingsdialog.ui'
+**
+** Created: Fri May 7 20:57:37 2010
+**      by: Qt User Interface Compiler version 4.6.2
+**
+** WARNING! All changes made in this file will be lost when recompiling UI file!
+********************************************************************************/
+
+#ifndef UI_EVESETTINGSDIALOG_H
+#define UI_EVESETTINGSDIALOG_H
+
+#include <QtCore/QVariant>
+#include <QtGui/QAction>
+#include <QtGui/QApplication>
+#include <QtGui/QButtonGroup>
+#include <QtGui/QComboBox>
+#include <QtGui/QDialog>
+#include <QtGui/QDialogButtonBox>
+#include <QtGui/QHeaderView>
+#include <QtGui/QLabel>
+#include <QtGui/QLineEdit>
+#include <QtGui/QPushButton>
+#include <QtGui/QScrollArea>
+#include <QtGui/QWidget>
+
+QT_BEGIN_NAMESPACE
+
+class Ui_EveSettingsDialog
+{
+public:
+    QDialogButtonBox *buttonBox;
+    QLabel *label_4;
+    QScrollArea *scrollArea;
+    QWidget *scrollAreaWidgetContents;
+    QLineEdit *apiKeyEdit;
+    QLabel *label;
+    QLabel *label_2;
+    QLineEdit *userIdEdit;
+    QComboBox *characterCombo;
+    QLabel *label_3;
+    QPushButton *getCharactersButton;
+
+    void setupUi(QDialog *EveSettingsDialog)
+    {
+        if (EveSettingsDialog->objectName().isEmpty())
+            EveSettingsDialog->setObjectName(QString::fromUtf8("EveSettingsDialog"));
+        EveSettingsDialog->resize(800, 300);
+        buttonBox = new QDialogButtonBox(EveSettingsDialog);
+        buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
+        buttonBox->setGeometry(QRect(710, 20, 81, 241));
+        buttonBox->setOrientation(Qt::Vertical);
+        buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
+        label_4 = new QLabel(EveSettingsDialog);
+        label_4->setObjectName(QString::fromUtf8("label_4"));
+        label_4->setGeometry(QRect(20, 10, 171, 34));
+        label_4->setOpenExternalLinks(true);
+        scrollArea = new QScrollArea(EveSettingsDialog);
+        scrollArea->setObjectName(QString::fromUtf8("scrollArea"));
+        scrollArea->setGeometry(QRect(10, 50, 701, 241));
+        scrollArea->setWidgetResizable(true);
+        scrollAreaWidgetContents = new QWidget();
+        scrollAreaWidgetContents->setObjectName(QString::fromUtf8("scrollAreaWidgetContents"));
+        scrollAreaWidgetContents->setGeometry(QRect(0, 0, 695, 235));
+        apiKeyEdit = new QLineEdit(scrollAreaWidgetContents);
+        apiKeyEdit->setObjectName(QString::fromUtf8("apiKeyEdit"));
+        apiKeyEdit->setGeometry(QRect(10, 10, 671, 61));
+        QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+        sizePolicy.setHorizontalStretch(0);
+        sizePolicy.setVerticalStretch(0);
+        sizePolicy.setHeightForWidth(apiKeyEdit->sizePolicy().hasHeightForWidth());
+        apiKeyEdit->setSizePolicy(sizePolicy);
+        QFont font;
+        font.setPointSize(24);
+        apiKeyEdit->setFont(font);
+        apiKeyEdit->setFrame(true);
+        label = new QLabel(scrollAreaWidgetContents);
+        label->setObjectName(QString::fromUtf8("label"));
+        label->setGeometry(QRect(20, 0, 39, 16));
+        label_2 = new QLabel(scrollAreaWidgetContents);
+        label_2->setObjectName(QString::fromUtf8("label_2"));
+        label_2->setGeometry(QRect(20, 70, 34, 16));
+        userIdEdit = new QLineEdit(scrollAreaWidgetContents);
+        userIdEdit->setObjectName(QString::fromUtf8("userIdEdit"));
+        userIdEdit->setGeometry(QRect(10, 90, 661, 61));
+        sizePolicy.setHeightForWidth(userIdEdit->sizePolicy().hasHeightForWidth());
+        userIdEdit->setSizePolicy(sizePolicy);
+        userIdEdit->setFont(font);
+        characterCombo = new QComboBox(scrollAreaWidgetContents);
+        characterCombo->setObjectName(QString::fromUtf8("characterCombo"));
+        characterCombo->setGeometry(QRect(20, 170, 70, 24));
+        label_3 = new QLabel(scrollAreaWidgetContents);
+        label_3->setObjectName(QString::fromUtf8("label_3"));
+        label_3->setGeometry(QRect(20, 153, 87, 16));
+        getCharactersButton = new QPushButton(scrollAreaWidgetContents);
+        getCharactersButton->setObjectName(QString::fromUtf8("getCharactersButton"));
+        getCharactersButton->setGeometry(QRect(540, 180, 121, 22));
+        scrollArea->setWidget(scrollAreaWidgetContents);
+
+        retranslateUi(EveSettingsDialog);
+        QObject::connect(buttonBox, SIGNAL(accepted()), EveSettingsDialog, SLOT(accept()));
+        QObject::connect(buttonBox, SIGNAL(rejected()), EveSettingsDialog, SLOT(reject()));
+
+        QMetaObject::connectSlotsByName(EveSettingsDialog);
+    } // setupUi
+
+    void retranslateUi(QDialog *EveSettingsDialog)
+    {
+        EveSettingsDialog->setWindowTitle(QApplication::translate("EveSettingsDialog", "EVE settings", 0, QApplication::UnicodeUTF8));
+        label_4->setText(QApplication::translate("EveSettingsDialog", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
+"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
+"p, li { white-space: pre-wrap; }\n"
+"</style></head><body style=\" font-family:'DejaVu Sans'; font-size:8pt; font-weight:400; font-style:normal;\">\n"
+"<table style=\"-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;\">\n"
+"<tr>\n"
+"<td style=\"border: none;\">\n"
+"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">To see character info visit<br /><a href=\"http://www.eveonline.com/api/\"><span style=\" text-decoration: underline; color:#0057ae;\">EVE Online account page</span></a></p></td></tr></table></body></html>", 0, QApplication::UnicodeUTF8));
+        label->setText(QApplication::translate("EveSettingsDialog", "API key", 0, QApplication::UnicodeUTF8));
+        label_2->setText(QApplication::translate("EveSettingsDialog", "UserID", 0, QApplication::UnicodeUTF8));
+        label_3->setText(QApplication::translate("EveSettingsDialog", "Choose character", 0, QApplication::UnicodeUTF8));
+        getCharactersButton->setText(QApplication::translate("EveSettingsDialog", "Fetch characters", 0, QApplication::UnicodeUTF8));
+    } // retranslateUi
+
+};
+
+namespace Ui {
+    class EveSettingsDialog: public Ui_EveSettingsDialog {};
+} // namespace Ui
+
+QT_END_NAMESPACE
+
+#endif // UI_EVESETTINGSDIALOG_H
diff --git a/src/ui_mainwindow.h b/src/ui_mainwindow.h
new file mode 100644 (file)
index 0000000..da7e401
--- /dev/null
@@ -0,0 +1,84 @@
+/********************************************************************************
+** Form generated from reading UI file 'mainwindow.ui'
+**
+** Created: Fri May 7 20:57:37 2010
+**      by: Qt User Interface Compiler version 4.6.2
+**
+** WARNING! All changes made in this file will be lost when recompiling UI file!
+********************************************************************************/
+
+#ifndef UI_MAINWINDOW_H
+#define UI_MAINWINDOW_H
+
+#include <QtCore/QVariant>
+#include <QtGui/QAction>
+#include <QtGui/QApplication>
+#include <QtGui/QButtonGroup>
+#include <QtGui/QHeaderView>
+#include <QtGui/QLabel>
+#include <QtGui/QMainWindow>
+#include <QtGui/QMenuBar>
+#include <QtGui/QPushButton>
+#include <QtGui/QStatusBar>
+#include <QtGui/QWidget>
+
+QT_BEGIN_NAMESPACE
+
+class Ui_MainWindow
+{
+public:
+    QWidget *centralwidget;
+    QPushButton *fetchButton;
+    QLabel *iconLabel;
+    QLabel *trainingLabel;
+    QMenuBar *menubar;
+    QStatusBar *statusbar;
+
+    void setupUi(QMainWindow *MainWindow)
+    {
+        if (MainWindow->objectName().isEmpty())
+            MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
+        MainWindow->resize(394, 236);
+        centralwidget = new QWidget(MainWindow);
+        centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
+        fetchButton = new QPushButton(centralwidget);
+        fetchButton->setObjectName(QString::fromUtf8("fetchButton"));
+        fetchButton->setGeometry(QRect(280, 150, 90, 22));
+        iconLabel = new QLabel(centralwidget);
+        iconLabel->setObjectName(QString::fromUtf8("iconLabel"));
+        iconLabel->setGeometry(QRect(80, 60, 70, 70));
+        trainingLabel = new QLabel(centralwidget);
+        trainingLabel->setObjectName(QString::fromUtf8("trainingLabel"));
+        trainingLabel->setGeometry(QRect(10, 150, 221, 20));
+        trainingLabel->setAlignment(Qt::AlignCenter);
+        MainWindow->setCentralWidget(centralwidget);
+        menubar = new QMenuBar(MainWindow);
+        menubar->setObjectName(QString::fromUtf8("menubar"));
+        menubar->setGeometry(QRect(0, 0, 394, 20));
+        MainWindow->setMenuBar(menubar);
+        statusbar = new QStatusBar(MainWindow);
+        statusbar->setObjectName(QString::fromUtf8("statusbar"));
+        MainWindow->setStatusBar(statusbar);
+
+        retranslateUi(MainWindow);
+
+        QMetaObject::connectSlotsByName(MainWindow);
+    } // setupUi
+
+    void retranslateUi(QMainWindow *MainWindow)
+    {
+        MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", 0, QApplication::UnicodeUTF8));
+        fetchButton->setText(QApplication::translate("MainWindow", "Fetch!", 0, QApplication::UnicodeUTF8));
+        iconLabel->setText(QApplication::translate("MainWindow", "TextLabel", 0, QApplication::UnicodeUTF8));
+        trainingLabel->setText(QString());
+    } // retranslateUi
+
+};
+
+namespace Ui {
+    class MainWindow: public Ui_MainWindow {};
+} // namespace Ui
+
+QT_END_NAMESPACE
+
+#endif // UI_MAINWINDOW_H
diff --git a/src/widget.cpp b/src/widget.cpp
new file mode 100644 (file)
index 0000000..29b80be
--- /dev/null
@@ -0,0 +1,114 @@
+#include "widget.h"
+#include <QPainter>
+#include "eveaccount.h"
+#include "evesettingsdialog.h"
+#include "eveskilltraining.h"
+#include "skilltree.h"
+#include <QtDebug>
+#include <QNetworkConfigurationManager>
+
+QTM_USE_NAMESPACE
+
+Widget::Widget(QWidget *parent)
+    : QLabel(parent),
+    m_character(NULL),
+    m_settings(new EveSettingsDialog(this)),
+    m_training(NULL),
+    m_net(new QNetworkConfigurationManager(this)),
+    m_skills(new SkillTree(this)),
+    m_model(NULL)
+{
+    setAlignment(Qt::AlignCenter);
+    setAttribute(Qt::WA_TranslucentBackground);
+    setGeometry(0,0,150,180);
+    connect(m_net,SIGNAL(onlineStateChanged(bool)),this,SLOT(onlineStateChanged(bool)));
+    m_skills->loadSkills();
+}
+
+Widget::~Widget()
+{
+    delete m_character;
+}
+
+
+QSize Widget::sizeHint() const
+{
+    return 2 * QLabel::sizeHint();
+}
+
+void Widget::paintEvent(QPaintEvent *event)
+{
+    QPainter p(this);
+    p.setBrush(QColor(0, 0, 0, 128));
+    p.setPen(Qt::NoPen);
+    p.drawRoundedRect(rect(), 25, 25);
+    p.setPen(Qt::white);
+    QFont dFont(p.font());
+    dFont.setPixelSize(15);
+    p.setFont(dFont);
+    if (m_character != NULL) {
+        QPoint iconLoc((150-64)/2,(150-64)/2);
+        if (m_character->characterIcon) {
+            p.drawPixmap(iconLoc,*(m_character->characterIcon));
+        }
+        QRect nameLoc(0,10,150,20);
+        p.drawText(nameLoc,Qt::AlignCenter,m_character->name);
+        if (m_training) {
+            QRect skillTitle(0,110,150,50);
+            p.drawText(skillTitle,Qt::AlignCenter|Qt::TextWordWrap,
+                       QString("%1 %2").arg(m_skills->skillName(m_training->typeId))
+                                       .arg(m_training->level));
+            QRect skillLoc(0,155,150,20);
+            p.drawText(skillLoc,Qt::AlignCenter,m_training->endTime.toString(Qt::SystemLocaleShortDate));
+        }
+
+    }
+    p.end();
+
+    QLabel::paintEvent(event);
+}
+
+void Widget::showSettingsDialog()
+{
+
+    int result = m_settings->exec();
+    if (result == QDialog::Accepted) {
+        m_character = m_settings->selectedCharacter();
+        m_model = m_settings->model();
+        qDebug() << "Got character " << m_character->name;
+        connect(m_character,SIGNAL(imageLoaded()),this,SLOT(skillReady()));
+        m_character->fetchImage();
+        qDebug() << "Fetch skills";
+        m_training = new EveSkillTraining(this);
+        m_training->setAccount(m_settings->model().data());
+        m_training->setCharacter(m_character);
+        connect(m_training,SIGNAL(finished()),this,SLOT(update()));
+
+    }
+}
+
+void Widget::skillReady()
+{
+    update();
+    m_training->fetchInfo();
+}
+
+void Widget::onlineStateChanged(bool online)
+{
+    qDebug() << "Online status changed, reloading info";
+    if (online) {
+        m_training->fetchInfo();
+    }
+}
+
+// TODO
+void Widget::loadSettings()
+{
+
+}
+
+// TODO
+void Widget::saveSettings()
+{
+
+}
diff --git a/src/widget.h b/src/widget.h
new file mode 100644 (file)
index 0000000..5100df3
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef WIDGET_H
+#define WIDGET_H
+
+#include <QtGui/QLabel>
+#include <QNetworkConfigurationManager>
+#include <QSharedPointer>
+
+QTM_USE_NAMESPACE
+
+class EveCharacter;
+class EveSettingsDialog;
+class EveSkillTraining;
+class SkillTree;
+class EveModel;
+
+class Widget : public QLabel
+{
+    Q_OBJECT
+
+public:
+    Widget(QWidget *parent = 0);
+    ~Widget();
+    QSize sizeHint() const;
+    void setCharacter(EveCharacter *aChar) { m_character = aChar; }
+    EveCharacter *character() { return m_character; }
+public slots:
+    void showSettingsDialog();
+    void paintEvent(QPaintEvent *event);
+    void skillReady();
+    void onlineStateChanged(bool online);
+    void loadSettings();
+    void saveSettings();
+
+private:
+    EveCharacter *m_character;
+    EveSettingsDialog *m_settings;
+    EveSkillTraining *m_training;
+    QNetworkConfigurationManager *m_net;
+    SkillTree *m_skills;
+    QSharedPointer<EveModel> m_model;
+};
+
+#endif // WIDGET_H
diff --git a/tests/main.cpp b/tests/main.cpp
new file mode 100644 (file)
index 0000000..d123dc7
--- /dev/null
@@ -0,0 +1,82 @@
+#include <QtCore/QCoreApplication>
+#include <QtTest/QtTest>
+#include <eveaccount.h>
+#include <evemodel.h>
+#include <eveskilltraining.h>
+#include "skilltree.h"
+#include <QFile>
+class EveModelTest: public QObject
+{
+    Q_OBJECT
+private slots:
+    void testXml();
+    void testSkills();
+    void testSkillTree();
+};
+
+#define GONTH_ID 1525655037
+
+void EveModelTest::testXml()
+{
+    EveModel model;
+    model.setApiKey("2A671E53172C4F8A878BB770BBABFBD3CFF31815927F4E5591EA6D5576456E50");
+    model.setUserId(4079475);
+    model.fetchAccounts();
+    QSignalSpy spy(&model,SIGNAL(accountsReady()));
+    QTest::qWait(1000);
+    QCOMPARE(spy.length(),1);
+    QCOMPARE(model.characters().length(),2);
+    EveCharacter e;
+    foreach (e,model.characters()) {
+        qDebug() << e.name << ":" << e.characterId;
+    }
+    e = model.characters()[0];
+    qDebug() << "e assigned";
+    QSignalSpy imagespy(&e,SIGNAL(imageLoaded()));
+    qDebug() << "spy installed";
+    e.fetchImage();
+    qDebug() << "fetchImage called";
+    QTest::qWait(3000);
+    qDebug() << "waited";
+    qDebug() << e.characterIcon;
+    QVERIFY(imagespy.length() == 1);
+    qDebug() << "Test done";
+}
+
+void EveModelTest::testSkills()
+{
+    EveModel model;
+    model.setApiKey("2A671E53172C4F8A878BB770BBABFBD3CFF31815927F4E5591EA6D5576456E50");
+    model.setUserId(4079475);
+    model.fetchAccounts();
+    QTest::qWait(1000);
+    EveCharacter e = model.characters()[0];
+    EveSkillTraining skill;
+    skill.setCharacter(&e);
+    skill.setAccount(&model);
+    skill.fetchInfo();
+    QTest::qWait(1000);
+    qDebug() << skill.startTime
+            << skill.endTime
+            << skill.typeId
+            << skill.startSkillpoints
+            << skill.destSkillpoints
+            << skill.level;
+}
+
+void EveModelTest::testSkillTree()
+{
+    SkillTree stree;
+    QSignalSpy spy(&stree,SIGNAL(skillsLoaded()));
+    stree.loadSkills();
+    QTest::qWait(5000);
+    QCOMPARE(spy.count(),1);
+    QCOMPARE(stree.skillName(3442),QString("Drone Interfacing"));
+    stree.save();
+    QVERIFY(QFile::exists("/var/tmp/skillCache"));
+    QFile::remove("/var/tmp/skillCache");
+}
+
+QTEST_MAIN(EveModelTest)
+
+#include "main.moc"
diff --git a/tests/tests.pro b/tests/tests.pro
new file mode 100644 (file)
index 0000000..91a933a
--- /dev/null
@@ -0,0 +1,26 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2010-05-02T08:36:06
+#
+#-------------------------------------------------
+
+QT       += core network
+
+TARGET = tests
+CONFIG   += console qtestlib
+CONFIG   -= app_bundle
+
+TEMPLATE = app
+
+INCLUDEPATH += ../src
+
+SOURCES += main.cpp
+SOURCES += ../src/eveaccount.cpp \
+../src/evemodel.cpp \
+../src/eveskilltraining.cpp \
+../src/skilltree.cpp
+
+HEADERS += ../src/eveaccount.h \
+           ../src/evemodel.h \
+           ../src/eveskilltraining.h \
+../src/skilltree.h