/* Documentation on YaST-GTK */

YaST basis

  Yast-core is a simple byte-code interpreted programming
  language. It is splitted into two threads: one with the
  interpreter that runs the YCP application, the other
  with the interface. This is so because a user may want
  to setup another machine while sitting confortably
  with its GTK+ interface.

  Yast-core offers an interface API, called YWidgets, that
  is then wrapped, by dynamic loading at run-time, by
  one of the available interface modules that the user
  specified. This code is one of these modules.

  Yast is usually distributed with the /sbin/yast script
  that automatically looks for the best interface for
  the environment.
  Yast-core is located at /usr/lib/YaST2/bin/y2base ,
  on the Suse distribution.

  You don't need to know to program YCP, but you should
  have a look at its API because it will give you some
  insight into the platform. Yast documentation:
  http://forgeftp.novell.com//yast/doc/SL10.1/tdg/

Code structure

  Hierarchy of some of the widgets:

   YGWidget                    YWidget
          |                          |
          |                         /|
          |                        / |
          |\       YContainerWidget  |
          |\\      /                 |
          |\\YGSplit                 |
          |\\YGEmpty                 |
          |\\YGSpacing               |
          | \YSSquash                |
          |  YGAlignment     /-------|
          |                 /        |
          |\          YLabel         |
          | \          /             |
          |  \---YGLabel             |
          |                          |
          |                          |
          |\                        /|
          | \             YTextEntry |
          |  YGLabeledWidget  /      |
          |  |    \          /       |
          |  |    YGTextEntry        |
          |  |                       |
          |   \                      |
          |    YGScrolledWidget  /---|
          |      \              /    |
          |       \    YRichText     |
          |        \    /            |
          |     YGRichText           |
          |                          |
          |..........................|


  As you may see, everything inherits from both YGWidget
  and YWidget. YWidget derivated classes provide virtual functions
  that we must fill. A pointer to the interface class wrapper may
  always be obtain with widgetRep().

  On the other hand, YGWidget provides a common set of functionaly
  with great value for code re-use and also makes the code more
  robust and flexible since it provides an infrastructure to the
  whole code.
  YGWidgets always creates an associated GtkWidget object, as asked
  by the class that inherits it, and that can be obtained via
  getWidget().

  Every YGWidget has an associated parent, with the single exception
  of YGDialog, that provides their children with a GtkFixed container
  that they sit upon. getFixed() is the recursive function that asks
  the parent for its associated GtkFixed. Container widgets replace
  getFixed() by one that returns a GtkFixed that they created, thus
  closing the cycle.

  YGLabeledWidget and YGSrolledWidget are simple children of
  YGWidget that create the asked widget with a label or inside
  a GtkScrolledWindow for code re-use value.

  The force behind the all thing is in YGUI. Its header has the declaration
  for the creation of all the widgets, while its implementation is done
  on the widgets files, and also specifies whatever one widget is
  supported or not. YGUI.cc has, for the most part, the code that switches
  between our code and the main YWidget, since we all use the same thread.

On specific widgets

  Layout widgets
    Yast-core is the one responsible for the layout geometry stuff.
    This is why we use GtkFixed and this means that this part of
    the code is almost inexistant.
    We still need to create those classes though; they provide
    a GtkFixed for their children to sit on top, but they have almost
    no code. That's because the required functions (that is, moveChild()
    and setSize()) are provided on YGWidget and inherited by them.

    YGAlignment
      As opposite to the other layouts, this one uses a GtkEventBox
      on bottom of the GtkFixed and overrides getFixed() as
      appropriate.
      This is because GtkFixed doesn't have an associated GtkWindow
      that we need because we may have to draw a background image
      if requested.

      YGFrame and YGDumbTab containers use the same approach.

  Most of the widgets are a no brainer. Any other widget should have
  some comments as appropriate.

Code practices

  As you may have noticed from YGWidget.h, we use macros a lot to
  enable common functionality, instead of virtual functions. This is
  because simply in a few cases virtual functions would not apply --
  ie. the need to call a parent class function. You would have to
  override the virtual function everytime. With a macro, we may do:
  #define YGLABEL_WIDGET_IMPL_SET_LABEL_CHAIN (ParentClass) \
    virtual setLabel (const YCPString &label) {             \
      doSetLabel (label);                                   \
      ParentClass::setLabel (label);                        \
    }

  The code style is heavily inspired on the Linux guideline:
  http://pantransit.reptiles.org/prog/CodingStyle.html
  There may be some differences as it obviously doesn't mention
  C++ classes.
  I also use tabs for identation and spaces for layout so that
  the code looks nice no matter your tab stops. But the only
  requirement is that it looks nice on 4 tab stops, so you may
  want to set that definition.

  Speaking of classes, we don't use headers, unless we have to, as
  opposite to the other interfaces code. There is no reason to
  make them publicaly available and it just adds even more
  files to the pool.
  Also, we generally don't split classes into declaration and
  implementation code, for the same reasons.

  Variables and functions names follow Yast style (variableNameOne),
  for the most part. Our Gtk widgets (on ygtk* files) are written
  in C and follow this_name_style to fit better with GTK+ one.
  We always prefix m_ to class memeber variables.

Morphing Suse to use Yast-GTK

  We haven't yet managed to create a Suse GTK-based installer, but
  these steps should be close to that end.

  1. Pick the 1st CD of some Suse edition version ISO
     (let's say it is named SUSE.iso)

  2. Mount it
  $ mkdir cd
  $ sudo mount -o loop -t iso9660 SUSE.iso cd

  3. Mount the 'root' file
  $ mkdir root
  $ sudo mount -o -t auto cd/boot/i386/root root

  4. Clone them so you can write to them
  $ cp -rp cd my_cd

  $ mkdir my_root
  $ cd root ; sudo tar cf ../my_root/root.tar ; cd ..
  $ cd my_root ; sudo tar xf root.tar ; sudo rm -f root.tar ; cd ..

  $ sudo chmod -R a+rw my_cd my_root
  (the usage of tar is because it keeps hard-links, unlike a "cp -rp".
   Ignore tar warnings about the timestamp -- this is related to the 'root'
   file's filesystem.)

  5. You may unmount them now (it will be done at shutdown anyway...)
  $ sudo umount cd root

  6. Copy GTK plugin to the CD:
  $ cp -p /usr/lib/YaST2/plugin/libpy2gtk.* my_root/usr/lib/Yast2/plugin

  7. Copy GTK plugin required libraries into the my_cd
  "ldd /usr/lib/YaST2/plugin/libpy2gtk.so" should give you an idea of
  which these are.

  8. Edit my_root/usr/lib/YaST2/startup/YaST2.class to replace qt by gtk
  (careful about automatic replaces: set_inst_qt_env and clr_inst_qt_env are
   executables, so better let them be for now...).
  You may use this one: http://rpmcruz.planetaclix.pt/trash/YaST2.call

  9. Remove a few (and be generous to avoid getting here again) packages from
     my_cd/suse/i586 (because the cd is already very tight...)

    Now, everything should be done. We just need to create our iso...

  10. Get cramfs (not included on suse cds, just google it and it will be 1st
      result), and compile it.

  11. Make your own 'root' file based on the changes from my_root
  $ rmdir root   # in case there is one...
  $ sudo cramfs/mkcramfs my_root root

  12. Copy your 'root' into the CD directory
  $ cp -f root my_cd/boot/i386/root

  13. Create the CD iso (without breaklines)
  $ cd my_cd
  $ sudo mkisofs -p "Yast-GTK installer" -publisher "Fools, Inc."
     -r -J -f -pad -no-emul-boot -boot-load-size 4 -boot-info-table
    -b boot/i386/loader/isolinux.bin -c boot/i386/boot.catalog -hide
    boot/i386/boot.catalog -hide-joliet boot/i386/boot.catalog -A
    "SUSE-Yast-GTK#0" -V SU1010.001 -o ../SUSE-Yast-GTK.iso .

  (This command is based on the one used to made Suse CD -- that you may
   get with a "strings SUSE.iso | grep mkisofs")

  14. Ready to burn and try.

LibZypp API overview

  The Zypp library is a wrapper around at least two package repository
  systems (zmd and rug). It is quite a monster with no clear API or
  documentation, thus this section.

  The first thing we need to do when using Zypp is to initializate it.
  This is done for us by yast-core, so we don't have to care about that.

  To get a list of packages, you iterate the Zypp pool, that you can get
  with zypp::getZYpp()->poolProxy(). So, if you want to iterate through
  the package pool, you would do:
    for (zypp::ResPoolProxy::const_iterator it =
           zypp::getZYpp()->poolProxy().byKindBegin <zypp::Package>();
         it != zypp::getZYpp()->poolProxy().byKindEnd <zypp::Package>(); it++)
  To iterate through patches, use zypp::Patch as the kind.

  The iterators point to objects of type zypp::ui::Selectable::Ptr, where Ptr
  is a boost intrusive_ptr -- so, you may turn that into a C pointers with
  a "zypp::ui::Selectable *sel = get_pointer (*it);"

  A Selectable object contains pointers to the installed version and all
  available (through repositories) versions of some packages whose name
  you may get with a zypp::ui::Selectable::name(). The package it considers
  as the best from the available packages is what it calls a candidate.
  Check /usr/include/zypp/ui/Selectable.h for its API.

  You may get more details on a given packages by retriving the correspondent
  zypp::ResObject from the Selectable of the version you want.
  Check /usr/include/zypp/ResObject.h for its API.

  To get even more information, you may cast the zypp::ResObject to a
  zypp::Package or zypp::Patch, according to what you are iterating.
  Since this is boost pointers we are dealing with, you would cast a
  zypp::ResObject::constPtr like
    zypp::dynamic_pointer_cast <const zypp::Package> (res_object);
  Check /usr/include/zypp/Package.h / Patch.h for their API.

  The reason why our categories tree is build in an ackward way is because you
  get the category of a package with zypp::Package::group() which returns a
  std::string like "Productivity/Networking/Email/Utilities". You can't build
  the tree before you start adding packages since you don't have a way to know
  its hierarchy.

  To avoid the lengthy names of Zypp, we use some typedefs and inline helper
  functions that you may find right on the start of YGPackageSelector.cc.
