diff options
Diffstat (limited to 'chrome/browser/wizard/wizard.cc')
-rw-r--r-- | chrome/browser/wizard/wizard.cc | 515 |
1 files changed, 515 insertions, 0 deletions
diff --git a/chrome/browser/wizard/wizard.cc b/chrome/browser/wizard/wizard.cc new file mode 100644 index 0000000..cce4f4a --- /dev/null +++ b/chrome/browser/wizard/wizard.cc @@ -0,0 +1,515 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/wizard/wizard.h" + +#include <math.h> + +#include "chrome/browser/wizard/wizard_step.h" +#include "chrome/browser/standard_layout.h" + +#include "chrome/common/l10n_util.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/common/stl_util-inl.h" + +#include "chrome/views/accelerator.h" +#include "chrome/views/label.h" +#include "chrome/views/native_button.h" + +#include "generated_resources.h" + +#include "SkBitmap.h" + +static const int kMinButtonWidth = 100; +static const int kWizardWidth = 400; +static const int kWizardHeight = 300; + +//////////////////////////////////////////////////////////////////////////////// +// +// WizardView is the Wizard top level view +// +//////////////////////////////////////////////////////////////////////////////// +class WizardView : public ChromeViews::View, + public ChromeViews::NativeButton::Listener { + public: + + WizardView(Wizard* owner) + : owner_(owner), + contents_(NULL), + custom_navigation_descriptor_(NULL) { + title_ = new ChromeViews::Label(L""); + title_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT); + title_->SetFont( + ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::LargeFont)); + AddChildView(title_); + + cancel_ = + new ChromeViews::NativeButton(l10n_util::GetString(IDS_WIZARD_CANCEL)); + AddChildView(cancel_); + cancel_->SetListener(this); + ChromeViews::Accelerator accelerator(VK_ESCAPE, false, false, false); + cancel_->AddAccelerator(accelerator); + + previous_ = new ChromeViews::NativeButton( + l10n_util::GetString(IDS_WIZARD_PREVIOUS)); + AddChildView(previous_); + previous_->SetListener(this); + + next_ = new ChromeViews::NativeButton(l10n_util::GetString(IDS_WIZARD_NEXT)); + AddChildView(next_); + next_->SetListener(this); + } + + virtual ~WizardView() { + // Views provided by WizardStep instances are owned by the step + if (contents_) { + RemoveChildView(contents_); + contents_ = NULL; + } + + // Sub views are deleted by the view system + } + + void ComputeButtonSize(int* width, int *height) { + *width = kMinButtonWidth; + CSize s; + cancel_->GetPreferredSize(&s); + *height = s.cy; + *width = std::max(*width, static_cast<int>(s.cx)); + + previous_->GetPreferredSize(&s); + *width = std::max(*width, static_cast<int>(s.cx)); + *height = std::max(*height, static_cast<int>(s.cy)); + + next_->GetPreferredSize(&s); + *width = std::max(*width, static_cast<int>(s.cx)); + *height = std::max(*height, static_cast<int>(s.cy)); + } + + virtual void Layout() { + View* parent = GetParent(); + DCHECK(parent); + if (!parent) + return; + + if (title_->GetText().empty()) { + title_->SetBounds(0, 0, 0, 0); + title_->SetVisible(false); + } else { + CSize s; + title_->SetVisible(true); + title_->GetPreferredSize(&s); + title_->SetBounds(kPanelHorizMargin, kPanelVertMargin, + GetWidth() - (2 * kPanelVertMargin), + s.cy); + } + + int bw, bh; + ComputeButtonSize(&bw, &bh); + + int button_y = GetHeight() - kPanelVertMargin - bh; + cancel_->SetBounds(kPanelHorizMargin, button_y, bw, bh); + next_->SetBounds(GetWidth() - kPanelHorizMargin - bw, button_y, bw, bh); + previous_->SetBounds(next_->GetX() - kPanelHorizMargin - bw, button_y, bw, bh); + + if (contents_) { + int y = title_->GetY() + title_->GetHeight() + kPanelVertMargin; + contents_->SetBounds(kPanelHorizMargin, y, + GetWidth() - (2 * kPanelHorizMargin), + cancel_->GetY() - kPanelVertMargin - y); + contents_->Layout(); + } + } + + virtual void GetPreferredSize(CSize* out) { + CSize s; + int w = 0, h = 0; + + if (contents_) { + int extra_margin = (2 * kPanelVertMargin); + contents_->GetPreferredSize(&s); + w = s.cx; + h = s.cy + extra_margin; + } + + if (!title_->GetText().empty()) { + title_->GetPreferredSize(&s); + w = std::max(w, static_cast<int>(s.cx)); + h += s.cy; + } + + int bw, bh; + ComputeButtonSize(&bw, &bh); + h += bh; + + w += (2 * kPanelHorizMargin); + h += (2 * kPanelVertMargin); + out->cx = std::max(kWizardWidth, w); + out->cy = std::max(kWizardHeight, h); + } + + virtual void ButtonPressed(ChromeViews::NativeButton *sender) { + if (sender == cancel_) + owner_->Cancel(); + else if (sender == next_) + owner_->SelectNextStep(); + else if (sender == previous_) + owner_->SelectPreviousStep(); + } + + typedef enum { + FIRST_STEP_STYLE, + NORMAL_STEP_STYLE, + LAST_STEP_STYLE, + CUSTOM_STYLE + } ContentsStyle; + + void SetContents(ChromeViews::View* v, ContentsStyle s) { + if (contents_) + RemoveChildView(contents_); + custom_navigation_descriptor_ = NULL; + contents_ = v; + + // Note: we change the navigation button only if a view is provided. + // if |v| is NULL, this wizard is closing and updating anything may + // cause a flash. + if (contents_) { + switch (s) { + case FIRST_STEP_STYLE: + previous_->SetVisible(false); + next_->SetVisible(true); + next_->SetLabel(l10n_util::GetString(IDS_WIZARD_NEXT)); + break; + case NORMAL_STEP_STYLE: + previous_->SetVisible(true); + next_->SetVisible(true); + next_->SetLabel(l10n_util::GetString(IDS_WIZARD_NEXT)); + break; + case LAST_STEP_STYLE: + previous_->SetVisible(true); + next_->SetVisible(true); + next_->SetLabel(l10n_util::GetString(IDS_WIZARD_DONE)); + break; + } + AddChildView(contents_); + cancel_->SetVisible(true); + + // Reset the button labels to the default values. + previous_->SetLabel(l10n_util::GetString(IDS_WIZARD_PREVIOUS)); + next_->SetLabel(l10n_util::GetString(IDS_WIZARD_NEXT)); + } + } + + void SetTitle(const std::wstring& title) { + title_->SetText(title); + } + + void NavigationDescriptorChanged() { + if (custom_navigation_descriptor_) { + SetCustomNavigationDescriptor(custom_navigation_descriptor_); + } + } + + void SetCustomNavigationDescriptor(WizardNavigationDescriptor* wnd) { + custom_navigation_descriptor_ = wnd; + if (wnd->custom_default_button_) { + if (wnd->default_label_.empty()) { + next_->SetVisible(false); + } else { + next_->SetVisible(true); + next_->SetLabel(wnd->default_label_); + } + } + + if (wnd->custom_alternate_button_) { + if (wnd->alternate_label_.empty()) { + previous_->SetVisible(false); + } else { + previous_->SetVisible(true); + previous_->SetLabel(wnd->alternate_label_); + } + } + + cancel_->SetVisible(wnd->can_cancel_); + } + + WizardNavigationDescriptor* GetCustomNavigationDescriptor() { + return custom_navigation_descriptor_; + } + + void EnableNextButton(bool f) { + if (next_) + next_->SetEnabled(f); + } + + bool IsNextButtonEnabled() { + if (next_) + return next_->IsEnabled(); + else + return false; + } + + void EnablePreviousButton(bool f) { + if (previous_) + previous_->SetEnabled(f); + } + + bool IsPreviousButtonEnabled() { + if (previous_) + return previous_->IsEnabled(); + else + return false; + } + private: + + ChromeViews::Label* title_; + ChromeViews::NativeButton* next_; + ChromeViews::NativeButton* previous_; + ChromeViews::NativeButton* cancel_; + ChromeViews::View* contents_; + WizardNavigationDescriptor* custom_navigation_descriptor_; + + Wizard* owner_; + DISALLOW_EVIL_CONSTRUCTORS(WizardView); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// Wizard implementation +// +//////////////////////////////////////////////////////////////////////////////// + +Wizard::Wizard(WizardDelegate* d) : delegate_(d), + view_(NULL), + state_(NULL), + selected_step_(-1), + is_running_(false) { +} + +Wizard::~Wizard() { + Abort(); + delete view_; + RemoveAllSteps(); + + delete state_; + + STLDeleteContainerPairSecondPointers(images_.begin(), images_.end()); +} + +void Wizard::SetState(DictionaryValue* state) { + delete state_; + state_ = state; +} + +DictionaryValue* Wizard::GetState() { + return state_; +} + +void Wizard::AddStep(WizardStep* s) { + steps_.push_back(s); +} + +int Wizard::GetStepCount() const { + return static_cast<int>(steps_.size()); +} + +WizardStep* Wizard::GetStepAt(int index) { + DCHECK(index >= 0 && index < static_cast<int>(steps_.size())); + return steps_[index]; +} + +void Wizard::RemoveAllSteps() { + DCHECK(!is_running_); + + int c = static_cast<int>(steps_.size()); + while (--c >= 0) + steps_[c]->Dispose(); + steps_.clear(); +} + +ChromeViews::View* Wizard::GetTopLevelView() { + if (!view_) { + view_ = new WizardView(this); + // We own the view and it should never be deleted by the parent + // view + view_->SetParentOwned(false); + } + return view_; +} + +void Wizard::Start() { + DCHECK(view_ && view_->GetParent()); + DCHECK(steps_.size() > 0); + DCHECK(!is_running_); + is_running_ = true; + SelectStepAt(0); + view_->Layout(); +} + +void Wizard::Reset() { + is_running_ = false; + if (selected_step_ != -1) + steps_[selected_step_]->WillBecomeInvisible(this, + WizardStep::CANCEL_ACTION); + selected_step_ = -1; + view_->EnableNextButton(true); + view_->EnablePreviousButton(true); + view_->SetContents(NULL, WizardView::NORMAL_STEP_STYLE); + + if (view_->GetParent()) + view_->GetParent()->RemoveChildView(view_); +} + +void Wizard::WizardDone(bool commit) { + if (is_running_) { + Reset(); + is_running_ = false; + if (delegate_) + delegate_->WizardClosed(commit); + } +} + +void Wizard::Abort() { + WizardDone(false); +} + +// Note: this private method doesn't call WillBecomeInvisible because +// it needs to be called before deciding what step to select next. +void Wizard::SelectStepAt(int index) { + DCHECK(index >= 0 && index < static_cast<int>(steps_.size())); + + if (index == selected_step_) + return; + + selected_step_ = index; + + WizardView::ContentsStyle style = WizardView::NORMAL_STEP_STYLE; + if (selected_step_ == 0) + style = WizardView::FIRST_STEP_STYLE; + else if (selected_step_ == (static_cast<int>(steps_.size()) - 1)) + style = WizardView::LAST_STEP_STYLE; + + view_->SetContents(steps_[selected_step_]->GetView(this), style); + steps_[selected_step_]->DidBecomeVisible(this); + WizardNavigationDescriptor* wnd = + steps_[selected_step_]->GetNavigationDescriptor(); + if (wnd) + view_->SetCustomNavigationDescriptor(wnd); + view_->SetTitle(steps_[selected_step_]->GetTitle(this)); + + CSize s; + view_->GetPreferredSize(&s); + if (view_->GetWidth() != s.cx || view_->GetHeight() != s.cy) + delegate_->ResizeTopLevelView(s.cx, s.cy); + view_->Layout(); + view_->SchedulePaint(); +} + +void Wizard::NavigationDescriptorChanged() { + view_->NavigationDescriptorChanged(); +} + +void Wizard::Cancel() { + Abort(); +} + +void Wizard::SelectStep(bool is_forward) { + int delta = is_forward ? 1 : -1; + WizardNavigationDescriptor* wnd = view_->GetCustomNavigationDescriptor(); + if (wnd) { + if (is_forward && wnd->custom_default_button_) + delta = wnd->default_offset_; + else if (!is_forward && wnd->custom_alternate_button_) + delta = wnd->alternate_offset_; + } + + if (selected_step_ != -1) { + steps_[selected_step_]->WillBecomeInvisible( + this, is_forward ? WizardStep::DEFAULT_ACTION : + WizardStep::ALTERNATE_ACTION); + } + + bool step_selected = false; + int i; + for (i = selected_step_ + delta; + i >= 0 && i < static_cast<int>(steps_.size()); i += delta) { + if (steps_[i]->IsEnabledFor(this)) { + SelectStepAt(i); + step_selected = true; + break; + } + } + + if (!step_selected && is_forward) { + WizardDone(true); + } +} + +void Wizard::SelectNextStep() { + SelectStep(true); +} + +void Wizard::SelectPreviousStep() { + SelectStep(false); +} + +void Wizard::SetImage(const std::wstring& image_name, SkBitmap* image) { + ImageMap::iterator i = images_.find(image_name); + if (i != images_.end()) + delete i->second; + images_[image_name] = image; +} + +SkBitmap* Wizard::GetImage(const std::wstring& image_name, bool remove_image) { + ImageMap::iterator i = images_.find(image_name); + SkBitmap* bitmap = NULL; + if (i != images_.end()) { + bitmap = i->second; + if (remove_image) { + images_.erase(i); + } + } + return bitmap; +} + +void Wizard::EnableNextStep(bool flag) { + view_->EnableNextButton(flag); +} + +bool Wizard::IsNextStepEnabled() const { + return view_->IsNextButtonEnabled(); +} + +void Wizard::EnablePreviousStep(bool flag) { + view_->EnablePreviousButton(flag); +} + +bool Wizard::IsPreviousStepEnabled() const { + return view_->IsPreviousButtonEnabled(); +} |