summaryrefslogtreecommitdiffstats
path: root/chrome/browser/wizard/wizard.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/wizard/wizard.cc')
-rw-r--r--chrome/browser/wizard/wizard.cc515
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();
+}