/* Documentation on YaST-GTK */ YaST basis Yast-core is a simple byte-code interpreted programming language. On execution, two threads will be fired: one for the interpreter that runs the YCP application, the other for 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 Old approach What yast-ncurses and -qt do, and we used to follow, is to let YWidget layout framework do that work for us. Every container widget we implemented had an associated GtkFixed that it returned with a getFixed(). Other widgets getFixed() would request the parent's getFixed(). However this approach was actually quite complicated, because we had to implement nicesize() and setSize() on container widgets in a way that the containee would be placed there correctly. So, for instance, for YGDumbTab, we had to request informations like the tabs labels height, the frame around the page, etc. Furthermore, the ultimate decision to drop the approach was when we redesigned YGtkWizard. This time the steps widget was dynamic and the work to check of any size change and report to YWidget just felt too hackish. Attempts at abstracting this were successful, but not very satisfactory from a code's point of view. Current approach Currently, we just implement the layout widgets ourselves. The most important is YGSplit, which would be the equivalent of GtkBox (though we use our YGtkRatioBox to support the weight childs attribute). The YGLayout code is just a little bigger than the previous, it's easy to understand, and overall it made the code base smaller. Furthermore, YWidgets had bugs like labels getting truncated (which had to be avoided by an explicit request for recalculating the whole layout geometry) that we don't suffer. The layout is also a lot faster. To set minimum size to widgets, to make them look prettier, we install all widgets in our YGtkMinSize container. To be able to honor the stretchable attribute, we also install this into a GtkAlignment. So, YGWidget's getWidget() returns the inner widget that is the one that our YWidget implementation ask it to create, while getLayout() should be used for layouts to get the outter widget, which would be the GtkAlignment. Furthermore the GtkAlignment allows us to align the widget on YGAlignment (which is just a proxy). 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. For indentations, tabs are used. For layout, spaces are used. Example: int variable_with_very_long_name = function (arg1, .............................................arg2); This ensures the code lays well in whatever editor the reader uses. Lines must just be ensured to be breaked before the 80th column on 4 stop tabs. 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. For GTK+ widgets, we follow some scheme. Most standing, we avoid declaring functions, and do the class initilization, where the overloading is set, at the bottom. When doing a widget, just copy and paste from another. 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. Ensure you have a live / working gtk+/Gnome install 3. Get cramfs (not included on suse cds, just google it and it will be 1st result), and compile it. 4. Tweak the variables in gtkize-iso to suit + you need a chunk of tmp space to unpack & re-build the iso 5. Run ./gtkize-iso SUSE.iso /tmp/new.iso 6. 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 (); it != zypp::getZYpp()->poolProxy().byKindEnd (); 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 (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.