layout.cpp

Go to the documentation of this file.
00001 /*
00002   libwt - Vassilis Virvilis Toolkit - a widget library
00003   Copyright (C) 2006 Vassilis Virvilis <vasvir2@fastmail.fm>
00004  
00005   This library is free software; you can redistribute it and/or
00006   modify it under the terms of the GNU Lesser General Public
00007   License as published by the Free Software Foundation; either
00008   version 2.1 of the License, or (at your option) any later version.
00009   
00010   This library is distributed in the hope that it will be useful,
00011   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013   Lesser General Public License for more details.
00014   
00015   You should have received a copy of the GNU Lesser General Public
00016   License along with this library; if not, write to the
00017   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018   Boston, MA  02111-1307, SA.
00019 */
00020 
00021 #include <wt/application.h>
00022 #include <wt/event.h>
00023 #include <wt/window.h>
00024 #include <wt/layout.h>
00025 
00026 namespace Wt {
00027 
00028 void LayoutItem::invalidateRecursively() {
00029     // if null means empty item. don't bother (used on Grid)
00030     // we can't handle it in isEmpty() because it is virtual
00031     // and the null dereference crashes
00032     trace("layout") << "Trying to invalidate " << this
00033     << " == "  << dynamic_cast<Object *>(this) << std::endl;
00034     if (isNonEmpty()) {
00035         trace("layout") << "Invalidating " << this << std::endl;
00036         LayoutIterator it = iterator();
00037         invalidated_ = true;
00038         for (it.start(); !it.finish(); it++) {
00039             LayoutItem *li = *it;
00040             li->invalidateRecursively();
00041         }
00042         trace("layout") << "Invalidation finished " << this << std::endl;
00043     }
00044 }
00045 
00046 int Layout::onSpacingOverride(int sp) {
00047     return (sp == -1) ? static_cast<int>(margin) : sp;
00048 }
00049 
00050 void Layout::adjustMaximumSize(int margin) {
00051     setMaximumSize(maximumSize() >> margin);
00052 }
00053 
00054 void Layout::init() {
00055     autoAdd.aboutToChange.connect(
00056         sigc::slot1<bool, bool>(
00057             sigc::mem_fun(*this, &Layout::onAboutToChangeAutoAdd)));
00058 
00059     spacing.override.connect(
00060         sigc::slot1<int, int>(
00061             sigc::mem_fun(*this, &Layout::onSpacingOverride)));
00062 
00063     sigc::slot0<void> post_layouthint_slot = sigc::mem_fun(*this,
00064             &Layout::postLayoutHintEvent);
00065 
00066     spacing.changed.connect(post_layouthint_slot);
00067 
00068     margin.changed.connect(
00069         sigc::slot1<void, int>(
00070             sigc::mem_fun(*this,
00071                           &Layout::adjustMaximumSize)));
00072     margin.changed.connect(post_layouthint_slot);
00073 
00074     adjustMaximumSize(margin);
00075 }
00076 
00077 Layout::Layout(Widget *parent, int margin, int spacing,
00078                const std::string &name)
00079         : Object(parent, name),
00080         LayoutItem(),
00081         margin(margin),
00082         spacing(spacing),
00083         autoAdd(false),
00084         deleted_li_(0),
00085 width_as_primary_length(true) {
00086     init();
00087     if (parent)
00088         parent->setLayout(this);
00089     Application::sendPostedEvents();
00090 }
00091 
00092 Layout::Layout(Layout * parent, int margin, int spacing,
00093                const std::string &name)
00094         : Object(parent, name),
00095         LayoutItem(),
00096         margin(margin),
00097         spacing(spacing),
00098         autoAdd(false),
00099         deleted_li_(0),
00100 width_as_primary_length(true) {
00101     init();
00102     parent->addItem(this);
00103     Application::sendPostedEvents();
00104 }
00105 
00106 Layout::Layout(int spacing, const std::string &name)
00107         : Object(0, name),
00108         LayoutItem(),
00109         margin(0),
00110         spacing(spacing),
00111         autoAdd(false),
00112         deleted_li_(0),
00113 width_as_primary_length(true) {
00114     init();
00115     Application::sendPostedEvents();
00116 }
00117 
00118 Widget *Layout::mainWidget() const {
00119     const Object *p = this;
00120     while ((p = p->parent())) {
00121         Widget *w = dynamic_cast<Widget *>(const_cast<Object *>(p));
00122         if (w) {
00123             return w;
00124         }
00125     }
00126     return 0;
00127 }
00128 
00129 bool Layout::isTopLevel() const {
00130     return (parent() && mainWidget() == parent());
00131 }
00132 
00133 bool Layout::onAboutToChangeAutoAdd(bool auto_add) {
00134     return (auto_add && !isTopLevel());
00135 }
00136 
00137 void Layout::postLayoutHintEvent() {
00138     if (mainWidget()) {
00139         Application::postEvent(mainWidget(), new LayoutHintEvent());
00140     }
00141 }
00142 
00143 void Layout::addItem(LayoutItem *li) {
00144     assert(!exists(li));
00145 
00146     // Here we care only for rogue add statements of fully created
00147     // objects and if they are layouts we put them under our
00148     // ownership. If li is already children of this then reparent
00149     // is a nop
00150     Layout *l;
00151     Object *child;
00152     if ((l = li->type<Layout *>())) {
00153         l->reparent(this);
00154     } else if ((child = li->type<Object *>())) {
00155         child->destroyed.connect(
00156             sigc::slot1<void, const Object *>(
00157                 sigc::mem_fun(*this, &Layout::on_wobject_item_death)));
00158     }
00159     postLayoutHintEvent();
00160 }
00161 
00162 bool Layout::removeItem(LayoutItem *li) {
00163     bool exist = exists(li);
00164     // It is possible to be asked to remove a non existing li
00165     if (exist) {
00166         // Delete only non Objects, Objects will be deleted by ~Object()
00167         // also delete child layouts here when removed but do not delete
00168         // them again if they are already deleted
00169 
00170         if (!isDeleted(li) && (li->is<Layout *>() || !li->is<Object *>())
00171            ) {
00172             delete li;
00173         }
00174 
00175         postLayoutHintEvent();
00176     }
00177     return exist;
00178 }
00179 
00180 void Layout::on_wobject_item_death(const Object *obj) {
00181     // we cannot do dynamic_cast because Widget does not exist anymore
00182     Widget *w = static_cast<Widget *>(const_cast<Object *>(obj));
00183     LayoutItem *li = static_cast<LayoutItem *>(w);
00184     // do not delete this bastard. It is alerady deleted
00185     markDeleted(li);
00186     removeItem(li);
00187 }
00188 
00189 /*!
00190   we get events from the mainWidget only if exists. The type of events are:
00191    - ChildEvent (Inserted or ChildRemoved)
00192    - ResizeEvent
00193    - LayoutHintEvent
00194  
00195    The following uses cases has to be covered:
00196     - case 1: A widget w deep in the hierarchy changes something
00197     (sizeHint, minimumSize, sizePolicy etc..)
00198     - case 2: insert/remove layout item
00199     - case 3: add remove child in a widget with a tll
00200     - case 4: parent widget gets resized
00201  
00202    \code
00203     // we monitor the managed widgets
00204     // top level layout grabs it (if tll exists)
00205     Layout::eventFilter() {
00206         // on layoutHint event calls
00207         Layout::enforce();
00208  
00209         // on resize calls
00210         Layout::setGeometry(r);
00211  
00212         // on child insert / remove in mainWidget
00213         // this implies enforce called in the next event processing
00214             Application::postEvent(mainWidget(), new LayoutHintEvent());
00215     }
00216    \endcode
00217     
00218    \note
00219     - if there is no top level layout then layout is not
00220     automatically enforced
00221     - use cases 1,2,3,4 are working if we assume the presence of a tll
00222     - we do a lot of invalidateRecursively()
00223     - widgets can only have one layout child (setLayout(), layout()) and
00224     no more
00225     - a widget cannot tell if it is managed or not
00226     - Layout::setGeometry() reimplementations must call base class implementation
00227  */
00228 bool Layout::eventFilter(Object *obj, Event *e) {
00229     Widget *w;
00230 
00231     switch (e->type()) {
00232     case Event::ChildInserted:
00233         if (autoAdd) {
00234             ChildEvent *ce = static_cast<ChildEvent *>(e);
00235             Object *child = ce->child();
00236             // It is not possible to add other widgets layout. They will
00237             // be toplevels and that means we are dead
00238             LayoutItem *li = dynamic_cast<LayoutItem *>(child);
00239 
00240             //assert(li == this);
00241             // we can only insert Widget items as childs of our mainWidget
00242             const bool is_window_frame = dynamic_cast<Window::Frame *>(child);
00243             const Window *wnd = dynamic_cast<Window *>(child);
00244             const bool is_frameless_window = wnd &&
00245                                              !dynamic_cast<Window::Frame *>(wnd->parent());
00246             if (li != this && !is_window_frame && !is_frameless_window
00247                     && (w = dynamic_cast<Widget *>(child))) {
00248                 trace("layout") << "Inserting to layout "
00249                 << this << " child " << w << std::endl;
00250                 addItem(w);
00251             }
00252         }
00253         break;
00254     case Event::ChildRemoved: {
00255             // we have to handle the reparenting case.
00256             // delete is handled gracefully. Problem is we don't
00257             // know if it is deleted. But if it is deleted it will
00258             // have been already removed. So we use static_cast to
00259             // go up and down the inheritance tree. It is also possible
00260             // that we don't manage this child.
00261             ChildEvent *ce = static_cast<ChildEvent *>(e);
00262             w = static_cast<Widget *>(ce->child());
00263             LayoutItem *li = static_cast<LayoutItem *>(w);
00264             // if item is already dead (removed) removeItem will do nothing
00265             removeItem(li);
00266         }
00267         break;
00268     case Event::Resize:
00269         w = dynamic_cast<Widget *>(obj);
00270         trace("layout") << "Widget Resize: " << w
00271         << " caused layout resize" << std::endl;
00272         setGeometry(w->geometry());
00273         break;
00274     case Event::Move:
00275         w = dynamic_cast<Widget *>(obj);
00276         trace("layout") << "Widget move: " << w
00277         << " caused layout move" << std::endl;
00278         LayoutItem::setGeometry(w->geometry());
00279         break;
00280     case Event::LayoutHint:
00281         enforce();
00282         break;
00283     default:
00284         break;
00285     }
00286 
00287     return Object::eventFilter(obj, e);
00288 }
00289 
00290 void Layout::setGeometry(const Rect& r) {
00291     Rect widget_rect = r;
00292 
00293     if (isTopLevel()) {
00294         const Rect& layout_rect = r >> margin;
00295         Size new_size = layout_rect.size();
00296 
00297         trace("layout") <<  "Is Widget hidden ? "
00298         << mainWidget()->isHidden() << std::endl;
00299         trace("layout") << this << " requested new_size " << new_size
00300         << " min_size " << minimumSize()
00301         << " hint_size " << sizeHint() << std::endl;
00302 
00303         if (isTrace("layout")) {
00304             LayoutIterator it = iterator();
00305             for (it.start(); !it.finish(); it++) {
00306                 LayoutItem *li = *it;
00307                 if (li->is<Object *>()) {
00308                     trace("layout") << "Listing item " << dynamic_cast<Object *>(li)
00309                     << " min_size " << li->minimumSize()
00310                     << " hint_size " << li->sizeHint() << std::endl;
00311                 }
00312             }
00313         }
00314 
00315         if (!new_size.contains(minimumSize())) {
00316             new_size = minimumSize().expandedTo(new_size);
00317         }
00318 
00319         if (!maximumSize().contains(new_size)) {
00320             new_size = new_size.boundedTo(maximumSize());
00321         }
00322 
00323         trace("layout") << this << " accepted new_size " << new_size << std::endl;
00324 
00325         widget_rect.setSize(new_size << margin);
00326         mainWidget()->setGeometry(widget_rect);
00327     }
00328     trace("layout") << this << " Layout::setGeometry()" << widget_rect << std::endl;
00329     // It is weird but in the layout::LayoutItem component
00330     // we store widget's geometry (<< margin)
00331     LayoutItem::setGeometry(widget_rect);
00332 }
00333 
00334 /// enforces the layout in the mainWidget() children and layouts
00335 /*! it can be only called if there is a main widget */
00336 void Layout::enforce() {
00337     const Rect rect = geometry();
00338     const Size min_size = minimumSize();
00339     const Size hint_size = sizeHint();
00340 
00341     // top downn invalidate across one widget only
00342     invalidateRecursively();
00343     // calls the virtual reimplementations
00344     setGeometry(rect);
00345     // this will notify parent of mainWidget() if exists
00346     if (rect != geometry() ||
00347             hint_size != sizeHint() ||
00348             min_size != minimumSize()) {
00349         mainWidget()->updateGeometry();
00350     }
00351 }
00352 
00353 void Layout::setWidthasPrimaryLength(bool width) {
00354     width_as_primary_length = width;
00355 }
00356 
00357 int Layout::preferredLength(const LayoutItem *li, bool expand) const {
00358     return primaryLength((expand) ? li->sizeHint() : li->minimumSize());
00359 }
00360 
00361 template<typename TYPE>
00362 int Layout::secondaryLength(const TYPE& t) const {
00363     return (width_as_primary_length) ? t.height() : t.width();
00364 }
00365 
00366 template<typename TYPE>
00367 int Layout::primaryOffset(const TYPE& t) const {
00368     return (width_as_primary_length) ? t.left() : t.top();
00369 }
00370 
00371 template<typename TYPE>
00372 int Layout::secondaryOffset(const TYPE& t) const {
00373     return (width_as_primary_length) ? t.top() : t.left();
00374 }
00375 
00376 Point Layout::getPoint(int primary_offset, int secondary_offset) const {
00377     return (width_as_primary_length) ?
00378            Point(primary_offset, secondary_offset) :
00379            Point(secondary_offset, primary_offset);
00380 }
00381 
00382 Size Layout::getSize(int primary, int secondary) const {
00383     return (width_as_primary_length) ?
00384            Size(primary, secondary) :
00385            Size(secondary, primary);
00386 }
00387 
00388 Rect Layout::getRect(int primary_offset, int secondary_offset,
00389                      int primary_size, int secondary_size) const {
00390     return (width_as_primary_length) ?
00391            Rect(primary_offset, secondary_offset, primary_size, secondary_size) :
00392            Rect(secondary_offset, primary_offset, secondary_size, primary_size);
00393 }
00394 
00395 int Layout::primaryStretch(const SizePolicy& sp) const {
00396     return (width_as_primary_length) ?
00397            sp.horizontalStretch : sp.verticalStretch;
00398 }
00399 
00400 bool Layout::mayGrowPrimally(const SizePolicy& sp) const {
00401     return (width_as_primary_length) ?
00402            sp.mayGrowHorizontally() : sp.mayGrowVertically();
00403 }
00404 
00405 bool Layout::mayShrinkPrimally(const SizePolicy& sp) const {
00406     return (width_as_primary_length) ?
00407            sp.mayShrinkHorizontally() : sp.mayShrinkVertically();
00408 }
00409 
00410 bool Layout::expandingPrimally(const SizePolicy& sp) const {
00411     return (width_as_primary_length) ?
00412            sp.expandingHorizontally() : sp.expandingVertically();
00413 }
00414 
00415 Size Layout::minimumSize() const {
00416     if (invalidated()) {
00417         Layout &self(*const_cast<Layout *>(this));
00418         self.validate();
00419     }
00420     return LayoutItem::minimumSize();
00421 }
00422 
00423 Size Layout::sizeHint() const {
00424     if (invalidated()) {
00425         Layout &self(*const_cast<Layout *>(this));
00426         self.validate();
00427     }
00428     return LayoutItem::sizeHint();
00429 }
00430 
00431 void BoxLayout::appendItem(LayoutItem *li) {
00432     Layout::addItem(li);
00433     push_back(li);
00434 }
00435 
00436 void BoxLayout::prependItem(LayoutItem *li) {
00437     Layout::addItem(li);
00438     push_front(li);
00439 }
00440 
00441 void BoxLayout::addItem(LayoutItem *li) {
00442     switch(direction) {
00443     case LeftToRight:
00444     case TopToBottom:
00445         appendItem(li);
00446         break;
00447     case RightToLeft:
00448     case BottomToTop:
00449         prependItem(li);
00450         break;
00451     default:
00452         break;
00453     }
00454 }
00455 
00456 /// let's calc again sizeHint, minimumSize etc
00457 void BoxLayout::validate() {
00458     int hint_p = 0, hint_s = 0;
00459     int min_p = 0, min_s = 0;
00460     int count = 0;
00461     int sp;
00462 
00463     trace("layout") << this << " BoxLayout::invalidate()" << std::endl;
00464 
00465     BoxLayoutIterator it(*this);
00466     for (it.start(); !it.finish(); it++) {
00467         LayoutItem *li = *it;
00468         if (!li->isEmpty()) {
00469             min_p += primaryLength(li->minimumSize());
00470             min_s = std::max(min_s, secondaryLength(li->minimumSize()));
00471             hint_p += primaryLength(li->sizeHint());
00472             hint_s = std::max(hint_s, secondaryLength(li->sizeHint()));
00473             trace("layout", "Up to item %d (%p) min_size %d hint_size %d\n", count, li, min_p, hint_p);
00474             count++;
00475         }
00476     }
00477     // don't forget spacing
00478     sp = spacing;
00479     if (count) {
00480         min_p += (count - 1) * sp;
00481         hint_p += (count - 1) * sp;
00482     }
00483 
00484     setSizeHint(getSize(hint_p, hint_s));
00485     setMinimumSize(getSize(min_p, min_s));
00486     Layout::validate();
00487 }
00488 
00489 /// fixup all managed widgets geometry
00490 void BoxLayout::setGeometry(const Rect& r) {
00491     BoxLayoutIterator it(*this);
00492     trace("layout") << this << " BoxLayout::setGeometry()" << r << std::endl;
00493 
00494     // store geometry first
00495     Layout::setGeometry(r);
00496     // if smaller than the minSize it is possible that
00497     // we have to reacquire the size to perform the layout
00498     Rect rect = LayoutItem::geometry() >> margin;
00499 
00500     // obvious optimization. If they are empty do nothing
00501     bool empty = true;
00502     for (it.start(); !it.finish(); it++) {
00503         LayoutItem *li = *it;
00504         if (!li->isEmpty()) {
00505             empty = false;
00506             break;
00507         }
00508     }
00509     if (empty)
00510         return;
00511 
00512     int expansion = primaryLength(rect) - primaryLength(sizeHint());
00513     bool expand = expansion >= 0;
00514 
00515     if (!expand) {
00516         expansion = primaryLength(rect) - primaryLength(minimumSize());
00517     }
00518 
00519     // we have to expand
00520     int stretch_sum = 0;
00521     int may_grow_num;
00522     int unclaimed_space;
00523 
00524     // initialize effective stretch to zero
00525     for (it.start(); !it.finish(); it++) {
00526         LayoutItem *li = *it;
00527         value(li).stretch = 0;
00528     }
00529 
00530     // first check if there are any stretch values
00531     may_grow_num = 0;
00532     unclaimed_space = 0;
00533     for (it.start(); !it.finish(); it++) {
00534         LayoutItem *li = *it;
00535         int li_stretch = primaryStretch(li->sizePolicy());
00536         if (!li->isEmpty() && li_stretch &&
00537                 mayGrowPrimally(li->sizePolicy())) {
00538             unclaimed_space += preferredLength(li, expand);
00539             stretch_sum += li_stretch;
00540             value(li).stretch = li_stretch;
00541             may_grow_num++;
00542         }
00543     }
00544 
00545     // check if we have to split the extra space among expanding
00546     if (!stretch_sum) {
00547         may_grow_num = 0;
00548         unclaimed_space = 0;
00549         for (it.start(); !it.finish(); it++) {
00550             LayoutItem *li = *it;
00551             if (!li->isEmpty() && expandingPrimally(li->sizePolicy())) {
00552                 unclaimed_space += preferredLength(li, expand);
00553                 value(li).stretch = 1;
00554                 may_grow_num++;
00555             }
00556         }
00557         stretch_sum = may_grow_num;
00558     }
00559 
00560     // check if we have to split the extra space among may Grow simply (preffered)
00561     if (!may_grow_num) {
00562         unclaimed_space = 0;
00563         for (it.start(); !it.finish(); it++) {
00564             LayoutItem *li = *it;
00565             if (!li->isEmpty() && mayGrowPrimally(li->sizePolicy())) {
00566                 unclaimed_space += preferredLength(li, expand);
00567                 value(li).stretch = 1;
00568                 may_grow_num++;
00569             }
00570         }
00571         stretch_sum = may_grow_num;
00572     }
00573 
00574     // none of the fuckers grows -- all fixed size
00575     if (!may_grow_num) {
00576         unclaimed_space = 0;
00577         for (it.start(); !it.finish(); it++) {
00578             LayoutItem *li = *it;
00579             if (!li->isEmpty()) {
00580                 unclaimed_space += preferredLength(li, expand);
00581                 value(li).stretch = 1;
00582                 may_grow_num++;
00583             }
00584         }
00585         stretch_sum = may_grow_num;
00586     }
00587 
00588     // Now we will remove offenders until we can distribute evenly
00589     int available_space;
00590     int claimed_space;
00591 restart:
00592     /// \todo find biggest offender not the first one
00593     available_space = expansion + unclaimed_space;
00594     claimed_space = 0;
00595     for (it.start(); !it.finish(); it++) {
00596         LayoutItem *li = *it;
00597         if (!li->isEmpty() && value(li).stretch) {
00598             int li_p = (value(li).stretch *
00599                         available_space) / stretch_sum;
00600             if (li_p < preferredLength(li, expand)) {
00601                 // problem: offender found, remove him
00602                 trace("layout", "Removing offender %p with "
00603                       "stretch %d stretch_sum %d\n",
00604                       li, value(li).stretch, stretch_sum);
00605                 unclaimed_space -= preferredLength(li, expand);
00606                 stretch_sum -= value(li).stretch;
00607                 value(li).stretch = 0;
00608                 may_grow_num--;
00609                 goto restart;
00610             }
00611             claimed_space += li_p;
00612         }
00613     }
00614 
00615     // now we have to distribute evenly the difference
00616     // that got truncated in the integer division
00617     int rest_space = available_space - claimed_space;
00618     int div = 0;
00619     int modulo = 0;
00620     if (may_grow_num) {
00621         div = rest_space / may_grow_num;
00622         modulo = rest_space % may_grow_num;
00623     }
00624 
00625     int count = 0;
00626     for (it.start(); !it.finish(); it++) {
00627         LayoutItem *li = *it;
00628         if (value(li).stretch) {
00629             value(li).extra_space = div + (count < modulo) ? 1 : 0;
00630         } else {
00631             value(li).extra_space = 0;
00632         }
00633         count++;
00634     }
00635 
00636     // finally place them
00637     int covered = primaryOffset(rect);
00638     int sp = spacing;
00639     for (it.start(); !it.finish(); it++) {
00640         LayoutItem *li = *it;
00641 
00642         if (li->isEmpty())
00643             continue;
00644 
00645         int space = ( (value(li).stretch) ?
00646                       (value(li).stretch * available_space) / stretch_sum :
00647                       preferredLength(li, expand) ) + value(li).extra_space;
00648         const Size& container_size = getSize(space,
00649                                              secondaryLength(rect));
00650         const Size& hint_size = li->sizeHint();
00651         const Size& min_size = li->minimumSize();
00652         const int w = (li->sizePolicy().mayGrowHorizontally())
00653                       ? container_size.width()
00654                       : (hint_size.width() <= container_size.width())
00655                       ? hint_size.width()
00656                       : min_size.width();
00657         const int h = (li->sizePolicy().mayGrowVertically())
00658                       ? container_size.height()
00659                       : (hint_size.height() <= container_size.height())
00660                       ? hint_size.height()
00661                       : min_size.height();
00662         const Size& cell_size = Size(w, h);
00663 
00664         trace("layout", "BoxLayout:: Allocating space %d for layoutItem %p\n", space, li);
00665         trace("layout") <<  "BoxLayout:: Container size: "
00666         << container_size << " cell_size " << cell_size << std::endl;
00667         li->setGeometry(
00668             Rect::align(Rect(mainWidget()->mapFromParent(getPoint(covered,
00669                              secondaryOffset(rect))), container_size),
00670                         cell_size, li->alignment));
00671         covered += space + sp;
00672     }
00673 }
00674 
00675 } // namespace

Generated Fri Jul 28 19:23:00 2006.
Copyright © 1998-2003 by the respective authors.

This document is licensed under the terms of the GNU Free Documentation License and may be freely distributed under the conditions given by this license.