Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages

background_wnd.cpp

00001 // background_wnd.cpp
00002 
00003 // Copyright (C) 2004 Steven Weiß (steven11@gmx.de)
00004 //
00005 // Permission to copy, use, sell and distribute this software is granted
00006 // provided this copyright notice appears in all copies.
00007 // Permission to modify the code and to distribute modified code is granted
00008 // provided this copyright notice appears in all copies, and a notice
00009 // that the code was modified is included with the copyright notice.
00010 //
00011 // This software is provided "as is" without express or implied warranty,
00012 // and with no claim as to its suitability for any purpose.
00013 
00014 // version history in version.txt
00015 
00016 
00017 #pragma once
00018 #include <background_wnd/detail/include.hpp>
00019 #include <background_wnd/background_wnd.hpp>
00020 
00021 #include <win32gui/window.hpp>                    // win32gui
00022 #include <win32gui/event_handler.hpp>
00023 
00024 
00025 namespace win32 { namespace gui {
00026 
00027 
00028 // ============================================================================================================= //
00029 // background_wnd-class
00030 
00061 // ========================================================================================================== //
00062 // Public interface
00063 
00064 // c'tor
00065 background_wnd::background_wnd() : cr_bkgnd_(RGB(255, 255, 255)), bRedraw_on_changes_(true), next_index_(0)
00066 {}
00067 
00069 void background_wnd::bk_color(COLORREF cr)
00070 {
00071      if (cr != cr_bkgnd_) {
00072           cr_bkgnd_ = cr;
00073           if (bRedraw_on_changes_)           // redraw
00074                redraw_painters();
00075      }         
00076 }
00077 
00079 COLORREF background_wnd::bk_color() const
00080 {
00081      return cr_bkgnd_;
00082 }
00083 
00085 unsigned background_wnd::add_painter(const painter_base& painter)
00086 {    
00087      return add_raw_painter(painter.clone());     
00088 }
00089 
00093 void background_wnd::delete_painter(unsigned idx)
00094 {
00095      coll_painters_.erase( find_painter_info_iterator(idx) );    
00096      if (bRedraw_on_changes_)                // redraw
00097           redraw_painters();
00098 }
00099 
00101 void background_wnd::delete_all_painters()
00102 {
00103      coll_painters_.clear();
00104      if (bRedraw_on_changes_)                // redraw
00105           redraw_painters();
00106 }
00107 
00111 painter_base& background_wnd::get_painter(unsigned idx) const
00112 {
00113      return find_painter_info(idx).get_painter();
00114 }
00115 
00116 
00121 std::vector<unsigned> background_wnd::get_indices() const
00122 {
00123      std::vector<unsigned> indices;
00124      indices.reserve(coll_painters_.size());
00125      for (iterator it = coll_painters_.begin(); it != coll_painters_.end(); ++it)
00126           indices.push_back( (*it)->idx_ );
00127      return indices;        
00128 }
00129 
00138 void background_wnd::drawing_rect(unsigned idx, const rectangle& rc)
00139 {      
00140      find_painter_info(idx).rc_drawing_ = rc;     
00141      if (bRedraw_on_changes_)                // redraw
00142           redraw_painters();
00143 }
00144 
00150 rectangle background_wnd::drawing_rect(unsigned idx) const
00151 {     
00152      return find_painter_info(idx).rc_drawing_;    
00153 }
00154 
00158 bool background_wnd::is_painter_visible(unsigned idx) const
00159 {
00160      return find_painter_info(idx).bVisible_;         
00161 }
00162 
00166 void background_wnd::show_painter(unsigned idx, bool bVisible)
00167 {        
00168      painter_info& info = find_painter_info(idx);
00169     if ( info.bVisible_ != bVisible) {
00170         info.bVisible_ = bVisible;
00171         if (bRedraw_on_changes_)                  // redraw
00172                redraw_painters();
00173     }
00174 }    
00175 
00180 void background_wnd::redraw_on_changes(bool bRedraw)
00181 {
00182      bRedraw_on_changes_ = bRedraw;
00183 }
00184 
00186 bool background_wnd::redraw_on_changes() const
00187 {
00188      return bRedraw_on_changes_;
00189 }
00190 
00192 void background_wnd::redraw_painters()
00193 {
00194      if (mem_dc_) {
00195           // fill background            
00196           HBRUSH hbr = CreateSolidBrush(cr_bkgnd_);
00197           FillRect(mem_dc_, &window_rect(rel_to_self), hbr);        
00198           DeleteObject((HGDIOBJ) hbr);    
00199 
00200           // redraw DC and invalidate window
00201           redraw_mem_dc();
00202           redraw();
00203      }
00204 }
00205 
00211 void background_wnd::transparency(unsigned idx, unsigned transparency)
00212 {
00213      painter_info& info = find_painter_info(idx);
00214 
00215      transparency = min<unsigned> (255, transparency);
00216      if (info.transparency_ != transparency) {
00217           info.transparency_ = transparency;
00218           if (bRedraw_on_changes_)           // redraw
00219                redraw_painters();
00220      }
00221 }
00222 
00226 unsigned background_wnd::transparency(unsigned idx) const
00227 {
00228      return find_painter_info(idx).transparency_;
00229 }
00230 
00232 // Z-Order functions
00233 // note: painters at the end of the sequence are on top of the Z-Order
00234 
00241 void background_wnd::zorder_to_front(unsigned idx)
00242 {  
00243      iterator it = find_painter_info_iterator(idx);
00244      if (it != coll_painters_.end() - 1) {        
00245         PainterInfoPtr p = *it;                   // lovely boost::shared_ptr's copy c'tor doesn't throw :-)
00246         coll_painters_.erase(it);
00247         coll_painters_.push_back(p);  
00248 
00249           if (bRedraw_on_changes_)           // redraw
00250                redraw_painters();
00251     }     
00252 }
00253 
00260 void background_wnd::zorder_to_back(unsigned idx)
00261 {    
00262      iterator it = find_painter_info_iterator(idx);
00263     if (it != coll_painters_.begin()) {
00264         PainterColl tmp;
00265         tmp.reserve(coll_painters_.size());
00266         tmp.push_back(*it);            
00267         std::copy( coll_painters_.begin(), it, std::back_inserter(tmp) );
00268           std::copy( it + 1, coll_painters_.end(), std::back_inserter(tmp) );
00269         coll_painters_.swap(tmp);            
00270 
00271           if (bRedraw_on_changes_)           // redraw
00272                redraw_painters();
00273     }
00274 }    
00275 
00282 void background_wnd::zorder_forward(unsigned idx)
00283 {    
00284      iterator it = find_painter_info_iterator(idx);
00285      if (it != coll_painters_.end() - 1) {
00286           std::swap(*it, *(it + 1));
00287           
00288           if (bRedraw_on_changes_)           // redraw
00289                redraw_painters();
00290      }
00291 }
00292 
00299 void background_wnd::zorder_backward(unsigned idx)
00300 {  
00301      iterator it = find_painter_info_iterator(idx);
00302      if (it != coll_painters_.begin()) {
00303           std::swap(*it, *(it - 1));
00304           
00305           if (bRedraw_on_changes_)           // redraw
00306                redraw_painters();
00307      }
00308 }
00309 
00310 
00311 // ========================================================================================================== //
00312 // Protected interface
00313 
00317 bg_wnd::memory_dc& background_wnd::get_mem_dc()
00318 {
00319     return mem_dc_;
00320 }
00321 
00325 const bg_wnd::memory_dc& background_wnd::get_mem_dc() const
00326 {
00327     return mem_dc_;
00328 }
00329 
00330 
00331 // ========================================================================================================== //
00332 // Implementation
00333 
00334 // Adds a painter and returns the index of this new painter
00335 unsigned background_wnd::add_raw_painter(painter_base* pPainter)
00336 {    
00337      try {          
00338           coll_painters_.push_back( PainterInfoPtr(new painter_info(next_index_, pPainter)) );
00339           ++next_index_;
00340      }
00341      catch (...) {
00342           delete pPainter;
00343           throw;
00344      }
00345 
00346      if (bRedraw_on_changes_)                // redraw
00347           redraw_painters();
00348      return next_index_ - 1;
00349 }
00350 
00351 // finds a painter_info by index
00352 background_wnd::iterator background_wnd::find_painter_info_iterator(unsigned idx) const
00353 {
00354      for (iterator it = coll_painters_.begin(); it != coll_painters_.end(); ++it) {
00355         if ((*it)->idx_ == idx)
00356             return it;
00357     }
00358      throw index_invalid(*this);             // no painter with this index -> throw    
00359 }
00360 
00361 // finds a painter_info by index
00362 background_wnd::painter_info& background_wnd::find_painter_info(unsigned idx) const
00363 {
00364      iterator it = find_painter_info_iterator(idx);
00365      return *( (*it).get() ); 
00366 }
00367 
00368 // creates the memory dc
00369 void background_wnd::create_mem_dc(HDC hDC)
00370 {
00371      if (mem_dc_ == NULL) {                  
00372         rectangle rc = window_rect(rel_to_self);
00373           COLORREF crOld = SetBkColor(hDC, cr_bkgnd_); // set background color so the memdc is filled with it ;-)
00374           mem_dc_.create_dc(hDC, rc.width(), rc.height());            
00375           SetBkColor(hDC, crOld);
00376 
00377         // draw memory dc
00378         redraw_mem_dc();
00379     }
00380 }
00381 
00382 // returns the actual rectangle in which a painter is drawn. if the drawing rect has a width or height of 0
00383 // or less the painter will be drawn in full client-rectangle size
00384 rectangle background_wnd::get_actual_drawing_rect(PainterInfoPtr pInfo) const
00385 {    
00386      // FIX_3
00387      // If drawing rect width/height is less than 0 now the width or the height are
00388      // separately scaled to client width/height
00389 
00390      // if drawing-rect is null take client-rect
00391      rectangle rc_painter = pInfo->rc_drawing_;
00392      rectangle rc_window = window_rect(rel_to_self);
00393      if (rc_painter.width() <= 0)
00394           rc_painter.right = rc_window.width();
00395      if (rc_painter.height() <= 0)
00396           rc_painter.bottom = rc_window.height();
00397      
00398      return rc_painter;
00399 }
00400 
00401 // determines if rc is totally overlapped by the rectangles stored in rects. it would be possible to use 
00402 // regions instead of this function but they're very slow
00403 bool background_wnd::is_overlapped(const rectangle& rc, const std::vector<rectangle>& rects)
00404 {         
00405      typedef std::vector<rectangle> RectangleCont;
00406      RectangleCont intersecting;
00407      unsigned area_rect = rc.height() * rc.width();
00408      unsigned area_total = 0;      
00409 
00410      // walk through all rectangles and accumulate the area intersecting rc
00411      for (RectangleCont::const_iterator it_added = rects.begin(); it_added != rects.end(); ++it_added) {                          
00412           rectangle  rc_intersect;
00413           if ( IntersectRect(&rc_intersect, &rc, &(*it_added)) ) {
00414 
00415                // because more than one added rectangles can overlap themselves we have to take care of that...
00416                for (RectangleCont::const_iterator it_intersected = intersecting.begin(); it_intersected != intersecting.end(); ++it_intersected) {                                                 
00417                     rectangle rc_temp;
00418                     if ( IntersectRect(&rc_temp, &rc_intersect, &(*it_intersected)) ) {
00419 
00420                          // if the intersection isn't big enough for SubtractRect() to succeed subtract the area of the
00421                          // intersection from area_total!
00422                          const int width = rc_temp.width();
00423                          const int height = rc_temp.height();
00424                          if (width < rc_intersect.width() && height < rc_intersect.height())
00425                               area_total -= width * height;                          
00426                          else
00427                               SubtractRect( &rc_intersect, &rc_intersect, &(*it_intersected) );
00428                     }
00429                }
00430 
00431                const int width = rc_intersect.width();
00432                const int height = rc_intersect.height();
00433                if (width && height) {
00434                     intersecting.push_back(rc_intersect);
00435                     area_total += rc_intersect.width() * rc_intersect.height();
00436                     
00437                     // early return?
00438                     if (area_total >= area_rect)
00439                          return true;
00440                }
00441           }              
00442      }
00443      return area_total >= area_rect;
00444 }
00445 
00446 // determines if a painter is partially visible. necessary for avoiding unnecessary redrawing
00447 bool background_wnd::is_partially_visible(const_iterator it_info, const rectangle& rc_painter) const
00448 {         
00449      // if painter invisible return immediately
00450      if ( (*it_info)->bVisible_ == false || (*it_info)->transparency_ == 0)
00451           return false;
00452      
00453      // test if this painter is overlapped by another painter which is 
00454      //        - higher in the z-order (for-loop)
00455      //        - has a bigger drawing_rect than this painter
00456      //        - visible (bVisible_ set to true)
00457      //        - opaque (transparency_ set to 255 and painter_base::is_opaque() returns true)
00458      //
00459      // note: it is possible that some painters TOGETHER overlap this painter...is_overlapped() checks this   
00460      std::vector<rectangle> rects_accumulated;         
00461      for (const_iterator it = it_info + 1; it != coll_painters_.end(); ++it) {
00462                          
00463           if ( (*it_info)->bVisible_ == true && (*it)->transparency_ == 255 && (*it)->get_painter().is_opaque(rc_painter.width(), rc_painter.height()) ) {
00464                rectangle rc = get_actual_drawing_rect(*it);
00465                ++rc.right; ++rc.bottom;           // increase by 1 because bottom right is excluded by contains_point()...
00466                if ( rc.contains_point(rc_painter.top_left()) && rc.contains_point(rc_painter.bottom_right()) )
00467                     return false;                 
00468                else {
00469                     --rc.right; --rc.bottom;      // reset rc
00470                     rects_accumulated.push_back(rc);                       
00471                }
00472           }
00473      }
00474      
00475      // no painter overlapped overlapped the passed painter by itself...so call is_overlapped() for the accumulated
00476      // painter drawing-rects
00477      return is_overlapped(rc_painter, rects_accumulated) == false;
00478 }
00479 
00480 // redraws the memory dc
00481 void background_wnd::redraw_mem_dc()
00482 {                   
00483     // Draw painters        
00484      for (iterator it = coll_painters_.begin(); it != coll_painters_.end(); ++it) {                                                         
00485           rectangle rc_painter = get_actual_drawing_rect(*it);
00486                     
00487           // only draw the painter if it is partially visible...
00488           if (is_partially_visible(it, rc_painter))         
00489                redraw_one_painter(*it, rc_painter);
00490      }
00491 }
00492 
00493 // Draws a painter on the memory DC  
00494 void background_wnd::redraw_one_painter(PainterInfoPtr pInfo, const rectangle& rc_painter)
00495 {        
00496      const int x = rc_painter.left;          const int y = rc_painter.top;
00497      const int cx = rc_painter.width(); const int cy = rc_painter.height();
00498 
00499      // FIX_2
00500      // because we now have a separate DC for the painter, it can't paint outside
00501      // its drawing-rect (before this fix the painter drawed directly on the memory
00502      // DC...). so the clipping works now correct! this change also affects the interface 
00503      // of painter_base: the draw()-function now doesn't need the x- and y-parameters - they're 
00504      // always 0!
00505      bg_wnd::memory_dc hPainterDC;
00506      hPainterDC.create_dc(get_mem_dc(), cx, cy);
00507 
00508      // FIX_1
00509      // if a painter was transparent and not all of the area of the drawing rect is drawn 
00510      // (-> transparent) it would be filled with the background-color of hTransparentDC.
00511      // because it is transparent you would see a colored glow...          
00512      get_mem_dc().bitblt(hPainterDC, 0, 0, cx, cy, x, y);
00513 
00514      // FIX_2
00515      // draw painter to painter DC      
00516      pInfo->get_painter().draw( hPainterDC, cx, cy );                 
00517 
00518      // painter is opaque so bitblt from the painter DC to the memory DC
00519      if (pInfo->transparency_ == 255) {                                    
00520           hPainterDC.bitblt(get_mem_dc(), x, y, cx, cy);
00521      }
00522      // painter is transparent so call AlphaBlend()
00523      else {
00524           BLENDFUNCTION bf;
00525           bf.BlendOp = AC_SRC_OVER;
00526           bf.BlendFlags = NULL;
00527           bf.SourceConstantAlpha = pInfo->transparency_;
00528           bf.AlphaFormat = 0;                     // ignore the alpha channel
00529           AlphaBlend(get_mem_dc(), x, y, cx, cy, hPainterDC, 0, 0, cx, cy, bf);           
00530      }
00531 }
00532       
00533 
00534 // ============================================================================================================= //
00535 // Handler for background_wnd
00536 
00537 struct background_wnd_handler : event_handler<background_wnd_handler, background_wnd>
00538 {
00539     handle_event on_erase_bkgnd(w_param<HDC> hDC, result res)
00540     {        
00541         // first make sure the memory DC is created
00542         window()->create_mem_dc(hDC);
00543           rectangle rc = window()->window_rect(rel_to_self);            
00544           window()->mem_dc_.bitblt(hDC, 0, 0, rc.width(), rc.height());
00545         
00546         res = true;        
00547         return event<WM_ERASEBKGND> ().HANDLED_BY(&me::on_erase_bkgnd);
00548     }
00549 
00550     handle_event on_size(l_param_lo cx, l_param_hi cy)
00551     {
00552           if (cx > 0 && cy > 0 && window()->mem_dc_) {
00553                SIZE size = window()->mem_dc_.size();
00554                    
00555                // If
00556                // (1) the window's size increases
00557                // (2) the window's size is FACTOR times smaller than the memory DC's
00558                // -> delete the memory DC, so it will be recreated in on_erase_bkgnd()
00559                const int FACTOR = 2;
00560                if (size.cx < cx || size.cy < cy || cx * FACTOR < size.cx || cy * FACTOR < size.cy)
00561                     window()->mem_dc_.delete_dc();               
00562           }
00563         return event<WM_SIZE> ().HANDLED_BY(&me::on_size);
00564     }
00565 };
00566 
00567 
00568 } }  // namespace win32::gui

Generated on Mon Dec 27 15:30:10 2004 for background_wnd by  doxygen 1.3.9.1