~ubuntu-branches/ubuntu/quantal/mysql-workbench/quantal

« back to all changes in this revision

Viewing changes to library/forms/winforms/src/wf_utilities.cpp

  • Committer: Package Import Robot
  • Author(s): Dmitry Smirnov
  • Date: 2012-03-01 21:57:30 UTC
  • Revision ID: package-import@ubuntu.com-20120301215730-o7y8av8y38n162ro
Tags: upstream-5.2.38+dfsg
ImportĀ upstreamĀ versionĀ 5.2.38+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
 * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU General Public License as
 
6
 * published by the Free Software Foundation; version 2 of the
 
7
 * License.
 
8
 * 
 
9
 * This program is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
12
 * GNU General Public License for more details.
 
13
 * 
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software
 
16
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 
17
 * 02110-1301  USA
 
18
 */
 
19
 
 
20
/**
 
21
 * Windows front end implementation of the mforms Utilities APIs.
 
22
 */
 
23
 
 
24
#include "stdafx.h"
 
25
 
 
26
#include <sstream>
 
27
#include <commctrl.h>
 
28
#include <StrSafe.h>
 
29
 
 
30
#include "base/string_utilities.h"
 
31
#include "wf_Utilities.h"
 
32
 
 
33
using namespace Windows::Forms;
 
34
using namespace System::Text;
 
35
using namespace System::Drawing;
 
36
using namespace System::Threading;
 
37
using namespace System::Diagnostics;
 
38
 
 
39
using namespace MySQL::Forms;
 
40
using namespace MySQL::Utilities;
 
41
 
 
42
//--------------------------------------------------------------------------------------------------
 
43
 
 
44
CustomMessageBox::CustomMessageBox()
 
45
{
 
46
  _picture= gcnew Windows::Forms::PictureBox();
 
47
  _button1= gcnew Windows::Forms::Button();
 
48
  _button2= gcnew Windows::Forms::Button();
 
49
  _button3= gcnew Windows::Forms::Button();
 
50
  _messageLabel= gcnew Windows::Forms::Label();
 
51
  _checkbox = gcnew Windows::Forms::CheckBox();
 
52
 
 
53
  SuspendLayout();
 
54
 
 
55
  Controls->Add(_picture);
 
56
  Controls->Add(_button2);
 
57
  Controls->Add(_button1);
 
58
  Controls->Add(_button3);
 
59
  Controls->Add(_messageLabel);
 
60
  Controls->Add(_checkbox);
 
61
 
 
62
  // picture
 
63
  _picture->Name= "picture";
 
64
  _picture->TabIndex= 0;
 
65
  _picture->TabStop= false;
 
66
 
 
67
  // button1
 
68
  _button1->Name= "button1";
 
69
  _button1->TabIndex= 1;
 
70
  _button1->UseVisualStyleBackColor= true;
 
71
  _button1->Click += gcnew System::EventHandler(this, &CustomMessageBox::ButtonClick);
 
72
 
 
73
  // button2
 
74
  _button2->Name= "button2";
 
75
  _button2->TabIndex= 2;
 
76
  _button2->UseVisualStyleBackColor= true;
 
77
  _button2->Click += gcnew System::EventHandler(this, &CustomMessageBox::ButtonClick);
 
78
 
 
79
  // button3
 
80
  _button3->Name= "button3";
 
81
  _button3->TabIndex= 3;
 
82
  _button3->UseVisualStyleBackColor= true;
 
83
  _button3->Click += gcnew System::EventHandler(this, &CustomMessageBox::ButtonClick);
 
84
 
 
85
  // messageText
 
86
  _messageLabel->Name= "messageLabel";
 
87
  _messageLabel->Padding= Windows::Forms::Padding(2);
 
88
  _messageLabel->TabIndex= 4;
 
89
  _messageLabel->Text= "label1";
 
90
 
 
91
  _checkbox->Name = "checkBox";
 
92
  _checkbox->TabIndex = 5;
 
93
 
 
94
  // Form
 
95
  this->Padding= Windows::Forms::Padding(8, 16, 8, 8);
 
96
  this->FormBorderStyle= ::FormBorderStyle::FixedDialog;
 
97
  this->AutoScaleDimensions= System::Drawing::SizeF(6, 13);
 
98
  this->AutoScaleMode= Windows::Forms::AutoScaleMode::Font;
 
99
  this->StartPosition = FormStartPosition::CenterScreen;
 
100
  this->Name= "CustomMessageBox";
 
101
  this->Text= "";
 
102
  this->ShowInTaskbar= false;
 
103
  this->HelpButton= false;
 
104
  this->MinimizeBox= false;
 
105
  this->MaximizeBox= false;
 
106
  //this->TopMost= true;
 
107
  this->Owner = UtilitiesImpl::get_mainform();
 
108
 
 
109
  ResumeLayout(false);
 
110
}
 
111
 
 
112
//--------------------------------------------------------------------------------------------------
 
113
 
 
114
// Some more task dialog icons, which are not defined in commctrl.h.
 
115
#define TD_SHIELD_BLUE_ICON      MAKEINTRESOURCE(-5)
 
116
#define TD_SECURITY_WARNING_ICON MAKEINTRESOURCE(-6)
 
117
#define TD_SECURITY_ERROR_ICON   MAKEINTRESOURCE(-7)
 
118
#define TD_SHIELD_SUCCESS_ICON   MAKEINTRESOURCE(-8)
 
119
#define TD_SHIELD_GRAY_ICON      MAKEINTRESOURCE(-9)
 
120
 
 
121
typedef HRESULT (WINAPI *PTaskDialogIndirect)(const TASKDIALOGCONFIG *pTaskConfig,
 
122
  __out_opt int *pnButton, __out_opt int *pnRadioButton, __out_opt BOOL *pfVerificationFlagChecked);
 
123
 
 
124
mforms::DialogResult CustomMessageBox::ShowVistaStyle(const std::string& title, const std::string& text,
 
125
  PCWSTR mainIcon, const std::string& buttonOK, const std::string& buttonCancel,
 
126
  const std::string& buttonOther, const std::string& checkbox, bool& checked)
 
127
{
 
128
  // Load TaskDialogIndirect dynamically so we don't get a problem on XP.
 
129
  HMODULE comctl32 = LoadLibraryW(L"comctl32.dll");
 
130
  PTaskDialogIndirect _TaskDialogIndirect = (PTaskDialogIndirect) GetProcAddress(comctl32, "TaskDialogIndirect");
 
131
 
 
132
  TASKDIALOGCONFIG config = {0};
 
133
  config.cbSize = sizeof(config);
 
134
 
 
135
  int button = 0;
 
136
  int radioButton = 0;
 
137
  BOOL verificationChecked = FALSE;
 
138
 
 
139
  int buttonCount = 0;
 
140
  TASKDIALOG_BUTTON buttons[2];
 
141
 
 
142
  std::wstring do_text = base::string_to_wstring(buttonOK);
 
143
  if (do_text.size() > 0)
 
144
  {
 
145
    buttons[buttonCount].nButtonID = IDOK;
 
146
    buttons[buttonCount].pszButtonText = do_text.c_str();
 
147
    buttonCount++;
 
148
  }
 
149
 
 
150
  // For cancel we show the in-built button, instead creating our own.
 
151
  // Additionally, we enable dialog cancellation.
 
152
  if (buttonCancel.size() > 0)
 
153
  {
 
154
    config.dwFlags |= TDF_ALLOW_DIALOG_CANCELLATION;
 
155
    config.dwCommonButtons = TDCBF_CANCEL_BUTTON;
 
156
  }
 
157
 
 
158
  std::wstring other_text = base::string_to_wstring(buttonOther);
 
159
  if (other_text.size() > 0)
 
160
  {
 
161
    buttons[buttonCount].nButtonID = 1000;
 
162
    buttons[buttonCount].pszButtonText = other_text.c_str();
 
163
    buttonCount++;
 
164
  }
 
165
 
 
166
  // If we have more than one button to show then enable command links.
 
167
  if (buttonCount > 1)
 
168
    config.dwFlags |= TDF_USE_COMMAND_LINKS;
 
169
 
 
170
  config.hwndParent = GetForegroundWindow();
 
171
  
 
172
  config.pszMainIcon = mainIcon;
 
173
  config.pszWindowTitle = L"MySQL Workbench";
 
174
 
 
175
  std::wstring titleText= base::string_to_wstring(title);
 
176
  config.pszMainInstruction = titleText.c_str();
 
177
  std::wstring descriptionText= base::string_to_wstring(text);
 
178
  config.pszContent = descriptionText.c_str();
 
179
  config.pButtons = buttons;
 
180
  config.cButtons = buttonCount;
 
181
  
 
182
  std::wstring checkbox_text = base::string_to_wstring(checkbox);
 
183
  if (checkbox_text.size() > 0)
 
184
    config.pszVerificationText = checkbox_text.c_str();
 
185
 
 
186
  HRESULT result = _TaskDialogIndirect(&config, &button, &radioButton, &verificationChecked);
 
187
  if (!SUCCEEDED(result))
 
188
    return mforms::ResultCancel;
 
189
 
 
190
  checked = verificationChecked == TRUE;
 
191
  switch (button)
 
192
  {
 
193
  case IDOK:
 
194
    return mforms::ResultOk;
 
195
    break;
 
196
  case IDCANCEL:
 
197
    return mforms::ResultCancel;
 
198
    break;
 
199
  default:
 
200
    return mforms::ResultOther;
 
201
  }
 
202
 
 
203
  FreeLibrary(comctl32);
 
204
}
 
205
 
 
206
//--------------------------------------------------------------------------------------------------
 
207
 
 
208
/// <summary>
 
209
/// Displays the CustomMessageBox and returns a dialog result, depending on the button clicked.
 
210
/// </summary>
 
211
mforms::DialogResult CustomMessageBox::ShowTraditionalStyle(String^ title, String^ text, Image^ image, String^ button1,
 
212
  String^ button2, String^ button3, String^ checkbox, bool& checked)
 
213
{
 
214
  CustomMessageBox^ box= gcnew CustomMessageBox();
 
215
  
 
216
  box->_picture->Image= image; 
 
217
  box->_picture->Size= image->Size;
 
218
  box->Text= title;
 
219
  box->_messageLabel->Text= text;
 
220
  box->_button1->Text= button1;
 
221
  box->_button1->DialogResult = Windows::Forms::DialogResult::OK;
 
222
  box->_button2->Text= button2;
 
223
  box->_button2->DialogResult = Windows::Forms::DialogResult::Cancel;
 
224
  box->_button3->Text= button3;
 
225
  box->_button3->DialogResult = Windows::Forms::DialogResult::Ignore;
 
226
  box->_checkbox->Text = checkbox;
 
227
  box->ComputeLayout();
 
228
 
 
229
  box->AcceptButton = box->_button1;
 
230
  box->CancelButton = box->_button2;
 
231
 
 
232
  Windows::Forms::DialogResult rc = box->ShowDialog();
 
233
 
 
234
  checked = box->_checkbox->Checked;
 
235
  switch (rc)
 
236
  {
 
237
  case Windows::Forms::DialogResult::OK:
 
238
    return mforms::ResultOk;
 
239
 
 
240
  default:
 
241
  case Windows::Forms::DialogResult::Cancel:
 
242
    return mforms::ResultCancel;
 
243
 
 
244
  case Windows::Forms::DialogResult::Ignore:
 
245
    return mforms::ResultOther;
 
246
  }
 
247
}
 
248
 
 
249
//--------------------------------------------------------------------------------------------------
 
250
 
 
251
// A little helper for repeating a button setup.
 
252
void AdjustButton(Windows::Forms::Button^ button)
 
253
{
 
254
  // Note: we need to set the Enabled property too as indicator if this button should be considered
 
255
  // when layouting the form. The Visible property cannot be used as long as the button's parent
 
256
  // is still hidden (which is the case when we do the layout process).
 
257
  if (button->Text == "")
 
258
  {
 
259
    button->Visible= false;
 
260
    button->Enabled= false;
 
261
  }
 
262
  else
 
263
  {
 
264
    Size size= button->GetPreferredSize(Drawing::Size::Empty);
 
265
    if (size.Width < 75)
 
266
      size.Width= 75;
 
267
    button->Size= size;
 
268
    button->Visible= true;
 
269
    button->Enabled= true;
 
270
  }
 
271
}
 
272
 
 
273
//--------------------------------------------------------------------------------------------------
 
274
 
 
275
#define MESSAGE_BOX_BUTTON_SPACING 8 // The spacing between two message buttons.
 
276
#define MESSAGE_BOX_MIN_WIDTH 300
 
277
#define MESSAGE_BOX_MIN_HEIGHT 128
 
278
 
 
279
/**
 
280
 * As the name already says this function computes the layout of the message box depending on the 
 
281
 * content (image size, button text etc.).
 
282
 */
 
283
void CustomMessageBox::ComputeLayout()
 
284
{
 
285
  SuspendLayout();
 
286
 
 
287
  // Buttons have a minimum width of 75 px. Make them all same size and hide those which don't have
 
288
  // any text.
 
289
  AdjustButton(_button1);
 
290
  AdjustButton(_button2);
 
291
  AdjustButton(_button3);
 
292
  _checkbox->Enabled = _checkbox->Text != "";
 
293
  _checkbox->Visible = _checkbox->Enabled; 
 
294
 
 
295
  int visibleButtonCount= 0;
 
296
  System::Drawing::Size size= System::Drawing::Size::Empty;
 
297
  if (_button1->Enabled)
 
298
  {
 
299
    size= _button1->Size;
 
300
    visibleButtonCount++;
 
301
  }
 
302
  if (_button2->Enabled)
 
303
  {
 
304
    size.Height= _button2->Height;
 
305
    if (_button2->Width > size.Width)
 
306
      size.Width= _button2->Width;
 
307
    visibleButtonCount++;
 
308
  }
 
309
  if (_button3->Enabled)
 
310
  {
 
311
    size.Height= _button3->Height;
 
312
    if (_button3->Width > size.Width)
 
313
      size.Width= _button3->Width;
 
314
    visibleButtonCount++;
 
315
  }
 
316
 
 
317
  // For the common height we use the one from the last visible button we found.
 
318
  // Since the font is assumed to be the same for all buttons they also should compute
 
319
  // the same height when asked for their preferred size.
 
320
  // Compute the total size of the button area on the way (including padding of the container).
 
321
  System::Drawing::Size buttonSize= System::Drawing::Size::Empty;
 
322
  if (size.Width > 0)
 
323
  {
 
324
    // Compute only if there is at least one visible button.
 
325
    buttonSize.Height= size.Height;
 
326
    if (_button1->Enabled)
 
327
    {
 
328
      _button1->Size= size;
 
329
      buttonSize.Width= size.Width;
 
330
    }
 
331
    if (_button2->Enabled)
 
332
    {
 
333
      _button2->Size= size;
 
334
      if (buttonSize.Width > 0)
 
335
        buttonSize.Width += MESSAGE_BOX_BUTTON_SPACING;
 
336
      buttonSize.Width += size.Width;
 
337
    }
 
338
    if (_button3->Enabled)
 
339
    {
 
340
      _button3->Size= size;
 
341
      if (buttonSize.Width > 0)
 
342
        buttonSize.Width += MESSAGE_BOX_BUTTON_SPACING;
 
343
      buttonSize.Width += size.Width;
 
344
    }
 
345
 
 
346
    // For the right spacing between button box and border.
 
347
    buttonSize.Width += MESSAGE_BOX_BUTTON_SPACING;
 
348
  }
 
349
 
 
350
  // Additional size if the checkbox is visible.
 
351
  int effective_button_width = buttonSize.Width;
 
352
  if (_checkbox->Enabled)
 
353
  {
 
354
    _checkbox->Size = _checkbox->PreferredSize;
 
355
    effective_button_width += Padding.Left + _checkbox->Size.Width + MESSAGE_BOX_BUTTON_SPACING;
 
356
  }
 
357
 
 
358
  // Compute message text layout. Start with a certain minimum width.
 
359
  int minWidth= (effective_button_width > MESSAGE_BOX_MIN_WIDTH) ? effective_button_width : MESSAGE_BOX_MIN_WIDTH;
 
360
  System::Drawing::Size proposedSize= System::Drawing::Size(minWidth, 1);
 
361
  
 
362
  // We use the golden ratio to create an appealing layout.
 
363
  // Increase width in 10% steps until we found the proper ratio.
 
364
  int lastWidth= 0;
 
365
  while (true)
 
366
  {
 
367
    size= _messageLabel->GetPreferredSize(proposedSize);
 
368
 
 
369
    // Terminate loop if we get a height of 0, the same width as in the last run, beyond a maximum width
 
370
    // or reached the golden ratio. Getting the same width means we never can get to the golden
 
371
    // ratio as the text is too small.
 
372
    if ((size.Height == 0) || (size.Width == lastWidth) || 
 
373
      (size.Width >= 1000) || ((float) size.Width / size.Height >= 1.618))
 
374
      break;
 
375
 
 
376
    lastWidth= size.Width;
 
377
    proposedSize.Width += proposedSize.Width / 10;
 
378
  };
 
379
 
 
380
  // GetPreferredSize might return a value smaller than our minimum width. Account for that.
 
381
  if (size.Width < minWidth)
 
382
    size.Width= minWidth;
 
383
  _messageLabel->Size= size;
 
384
 
 
385
  // Now that we have the text size compute the overall size of the message box.
 
386
  // The image is vertically centered at the left side (with some padding)
 
387
  // and the buttons are at the bottom (right aligned).
 
388
  int textHeight= _messageLabel->Padding.Vertical + _messageLabel->Height;
 
389
  if (textHeight < _picture->Height + _picture->Padding.Vertical)
 
390
    textHeight= _picture->Height + _picture->Padding.Vertical;
 
391
  size.Width= Padding.Horizontal + _picture->Padding.Horizontal + _picture->Width +
 
392
    _messageLabel->Padding.Horizontal + _messageLabel->Width;
 
393
  size.Height= Padding.Vertical + textHeight + buttonSize.Height;
 
394
 
 
395
  // Make sure we have good looking minimum height.
 
396
  if (size.Height < MESSAGE_BOX_MIN_HEIGHT)
 
397
    size.Height= MESSAGE_BOX_MIN_HEIGHT;
 
398
  ClientSize= size;
 
399
 
 
400
  // Move picture to its final location (center vertically over the message text's height).
 
401
  Point location;
 
402
  location.X= Padding.Left;
 
403
  location.Y= Padding.Top + (textHeight - _picture->Height - _picture->Padding.Vertical) / 2;
 
404
  _picture->Location= location;
 
405
 
 
406
  // Text location too.
 
407
  location.X= _picture->Right + _messageLabel->Padding.Left;
 
408
  location.Y= Padding.Top;
 
409
  _messageLabel->Location= location;
 
410
 
 
411
  // Move the buttons to their final locations.
 
412
  if (buttonSize.Width > 0)
 
413
  {
 
414
    location= Point(ClientSize.Width - buttonSize.Width, ClientSize.Height - buttonSize.Height - Padding.Bottom);
 
415
    if (_button1->Enabled)
 
416
    {
 
417
      _button1->Location= location;
 
418
      location.X += _button1->Width + MESSAGE_BOX_BUTTON_SPACING;
 
419
    }
 
420
    if (_button3->Enabled)
 
421
    {
 
422
      _button3->Location= location;
 
423
      location.X += _button3->Width + MESSAGE_BOX_BUTTON_SPACING;
 
424
    }
 
425
 
 
426
    // Button 2 is our Cancel button (the one which is triggered when ESC is pressed), so place it last.
 
427
    if (_button2->Enabled)
 
428
      _button2->Location= location;
 
429
  }
 
430
 
 
431
  // Display the checkbox on the same line as the buttons but left aligned (if visible).
 
432
  if (_checkbox->Enabled)
 
433
  {
 
434
    location.X = Padding.Left;
 
435
    location.Y += (buttonSize.Height - _checkbox->Size.Height) / 2;
 
436
    _checkbox->Location = location;
 
437
  }
 
438
 
 
439
  ResumeLayout(true);
 
440
}
 
441
 
 
442
//--------------------------------------------------------------------------------------------------
 
443
 
 
444
void CustomMessageBox::ButtonClick(Object^ sender, EventArgs^ arguments)
 
445
{
 
446
  DialogResult = ((Windows::Forms::Button^)sender)->DialogResult;
 
447
}
 
448
 
 
449
//--------------------------------------------------------------------------------------------------
 
450
 
 
451
mforms::DialogResult CustomMessageBox::Show(const std::string& title, const std::string& text,
 
452
  PCWSTR mainIcon, const std::string& buttonOK, const std::string& buttonCancel,
 
453
  const std::string& buttonOther, const std::string& checkbox, bool& checked)
 
454
{
 
455
  // Our message looks different depending on whether we are running on XP or Vista and above.
 
456
  if (ControlUtilities::IsVistaOrAbove())
 
457
  {
 
458
    mforms::Utilities::enter_modal_loop();
 
459
    mforms::DialogResult result = ShowVistaStyle(title, text, mainIcon, buttonOK, buttonCancel,
 
460
      buttonOther, checkbox, checked);
 
461
    mforms::Utilities::leave_modal_loop();
 
462
 
 
463
    return result;
 
464
  }
 
465
  else
 
466
  {
 
467
    String^ boxTitle= CppStringToNative(title);
 
468
    String^ boxText= CppStringToNative(text);
 
469
    String^ ok_text= CppStringToNative(buttonOK);
 
470
    String^ cancel_text= CppStringToNative(buttonCancel);
 
471
    String^ other_text= CppStringToNative(buttonOther);
 
472
    String^ checkbox_text= CppStringToNative(checkbox);
 
473
 
 
474
    Image^ image;
 
475
    if (mainIcon == TD_WARNING_ICON)
 
476
      image= Image::FromFile("images/ui/message_warning.png");
 
477
    else
 
478
      if (mainIcon == TD_ERROR_ICON)
 
479
        image= Image::FromFile("images/ui/message_error.png");
 
480
      else
 
481
        image= Image::FromFile("images/ui/message_confirm.png");
 
482
    
 
483
    mforms::Utilities::enter_modal_loop();
 
484
    mforms::DialogResult result = CustomMessageBox::ShowTraditionalStyle(boxTitle, boxText, image,
 
485
      ok_text, cancel_text, other_text, checkbox_text, checked);
 
486
    mforms::Utilities::leave_modal_loop();
 
487
    return result;
 
488
  }
 
489
}
 
490
 
 
491
//--------------------------------------------------------------------------------------------------
 
492
 
 
493
Windows::Forms::DialogResult CustomMessageBox::Show(MessageType type, String^ title, String^ text,  
 
494
  String^ buttonOK, String^ buttonCancel, String^ buttonOther, String^ checkbox, [Out] bool% checked)
 
495
{
 
496
  mforms::DialogResult result;
 
497
 
 
498
  // Our message looks different depending on whether we are running on XP or Vista and above.
 
499
  if (ControlUtilities::IsVistaOrAbove())
 
500
  {
 
501
    mforms::Utilities::enter_modal_loop();
 
502
    PCWSTR mainIcon;
 
503
    switch (type)
 
504
    {
 
505
    case MessageType::MessageWarning:
 
506
      mainIcon = TD_WARNING_ICON;
 
507
      break;
 
508
    case MessageType::MessageError:
 
509
      mainIcon = TD_ERROR_ICON;
 
510
      break;
 
511
    default:
 
512
      mainIcon = TD_INFORMATION_ICON;
 
513
      break;
 
514
    }
 
515
    bool isChecked = false;
 
516
    result = ShowVistaStyle(
 
517
      NativeToCppString(title), NativeToCppString(text), mainIcon, NativeToCppString(buttonOK),
 
518
      NativeToCppString(buttonCancel), NativeToCppString(buttonOther), NativeToCppString(checkbox),
 
519
      isChecked
 
520
    );
 
521
    checked = isChecked;
 
522
    mforms::Utilities::leave_modal_loop();
 
523
  }
 
524
  else
 
525
  {
 
526
    Image^ image;
 
527
    switch (type)
 
528
    {
 
529
    case MessageType::MessageWarning:
 
530
      image= Image::FromFile("images/ui/message_warning.png");
 
531
      break;
 
532
    case MessageType::MessageError:
 
533
      image= Image::FromFile("images/ui/message_error.png");
 
534
      break;
 
535
    default:
 
536
      image= Image::FromFile("images/ui/message_confirm.png");
 
537
      break;
 
538
    }
 
539
 
 
540
    mforms::Utilities::enter_modal_loop();
 
541
    bool isChecked = false;
 
542
    result = CustomMessageBox::ShowTraditionalStyle(title, text, image, buttonOK, buttonCancel,
 
543
      buttonOther, checkbox, isChecked);
 
544
    checked = isChecked;
 
545
    mforms::Utilities::leave_modal_loop();
 
546
  }
 
547
 
 
548
  Windows::Forms::DialogResult native_result;
 
549
  switch (result)
 
550
  {
 
551
  case mforms::ResultCancel:
 
552
    native_result = Windows::Forms::DialogResult::Cancel;
 
553
    break;
 
554
  case mforms::ResultOther:
 
555
    native_result = Windows::Forms::DialogResult::Ignore;
 
556
    break;
 
557
  default:
 
558
    native_result = Windows::Forms::DialogResult::OK;
 
559
    break;
 
560
  }
 
561
  return native_result;
 
562
}
 
563
 
 
564
//--------------------------------------------------------------------------------------------------
 
565
 
 
566
Windows::Forms::DialogResult CustomMessageBox::Show(MessageType type, String^ title, String^ text,
 
567
  String^ buttonOK)
 
568
{
 
569
  bool checked = false;
 
570
  return Show(type, title, text, buttonOK, "", "", "", checked);
 
571
}
 
572
 
 
573
//----------------- DispatchControl ----------------------------------------------------------------
 
574
 
 
575
delegate InvokationResult^ RunSlotDelegate();
 
576
 
 
577
void* DispatchControl::RunOnMainThread(const boost::function<void* ()>& slot, bool wait)
 
578
{
 
579
  if (InvokeRequired) // XXX handle wait here (if wait is false, it doesnt need to wait for the callback to finish executing
 
580
  {
 
581
    _slot = &slot;
 
582
    InvokationResult^ result = (InvokationResult^) Invoke(
 
583
      gcnew RunSlotDelegate(this, &DispatchControl::RunSlot));
 
584
    return result->Result;
 
585
  }
 
586
 
 
587
  return slot();
 
588
}
 
589
 
 
590
//--------------------------------------------------------------------------------------------------
 
591
 
 
592
/**
 
593
 * Helper function to run a given slot in the main thread.
 
594
 */
 
595
InvokationResult^ DispatchControl::RunSlot()
 
596
{
 
597
  return gcnew InvokationResult((*_slot)());
 
598
}
 
599
 
 
600
//----------------- UtilitiesImpl ------------------------------------------------------------------
 
601
 
 
602
UtilitiesImpl::UtilitiesImpl()
 
603
{
 
604
}
 
605
 
 
606
//--------------------------------------------------------------------------------------------------
 
607
 
 
608
int UtilitiesImpl::show_message(const string &title, const string &text, const string &ok, 
 
609
  const string &cancel, const string &other)
 
610
{
 
611
  bool checked;
 
612
  return CustomMessageBox::Show(title, text, TD_INFORMATION_ICON, ok, cancel, other, "", checked);
 
613
}
 
614
 
 
615
//--------------------------------------------------------------------------------------------------
 
616
 
 
617
int UtilitiesImpl::show_error(const string &title, const string &text, const string &ok, 
 
618
  const string &cancel, const string &other)
 
619
{
 
620
  bool checked;
 
621
  return CustomMessageBox::Show(title, text, TD_ERROR_ICON, ok, cancel, other, "", checked);
 
622
}
 
623
 
 
624
//--------------------------------------------------------------------------------------------------
 
625
 
 
626
int UtilitiesImpl::show_warning(const string &title, const string &text, const string &ok, 
 
627
  const string &cancel, const string &other)
 
628
{
 
629
  bool checked;
 
630
  return CustomMessageBox::Show(title, text, TD_WARNING_ICON, ok, cancel, other, "", checked);
 
631
}
 
632
 
 
633
//--------------------------------------------------------------------------------------------------
 
634
 
 
635
int UtilitiesImpl::show_message_with_checkbox(const string &title, const string &text,
 
636
  const string &ok, const string &cancel, const string &other, const std::string &checkbox_text,
 
637
  bool &isChecked)
 
638
{
 
639
  std::string checkboxText = (checkbox_text.size() > 0) ? checkbox_text :
 
640
    _("Don't show this message again");
 
641
  return CustomMessageBox::Show(title, text, TD_INFORMATION_ICON, ok, cancel, other, checkboxText,
 
642
 isChecked);
 
643
}
 
644
 
 
645
//--------------------------------------------------------------------------------------------------
 
646
 
 
647
/**
 
648
 * Shows the warning heads-up-display with the given title and text.
 
649
 */
 
650
void UtilitiesImpl::show_wait_message(const string &title, const string &text)
 
651
{
 
652
  HUDForm::Show(CppStringToNative(title), CppStringToNative(text), true);
 
653
}
 
654
 
 
655
//--------------------------------------------------------------------------------------------------
 
656
 
 
657
/**
 
658
 * Hides a previously shown wait message.
 
659
 */
 
660
bool UtilitiesImpl::hide_wait_message()
 
661
{
 
662
  bool result= HUDForm::IsVisible;
 
663
  if (result)
 
664
    HUDForm::Finished();
 
665
  return result;
 
666
}
 
667
 
 
668
//--------------------------------------------------------------------------------------------------
 
669
 
 
670
ref class CallSlotDelegate
 
671
{
 
672
public:
 
673
  CallSlotDelegate(const boost::function<bool ()> *s) : slot(s) {}
 
674
  const boost::function<bool ()> *slot;
 
675
  bool call_slot()
 
676
  {
 
677
    return (*slot)();
 
678
  }
 
679
};
 
680
 
 
681
//-------------------------------------------------------------------------------------------------
 
682
 
 
683
bool UtilitiesImpl::run_cancelable_wait_message(const string &title, const string &text, 
 
684
                                                const boost::function<void ()> &start_task,
 
685
                                                const boost::function<bool ()> &cancel_slot)
 
686
{
 
687
  CallSlotDelegate ^caller = gcnew CallSlotDelegate(&cancel_slot);
 
688
 
 
689
  HUDForm::CancelDelegate ^deleg = gcnew HUDForm::CancelDelegate(caller, &CallSlotDelegate::call_slot);
 
690
  if (start_task)
 
691
    start_task();
 
692
 
 
693
  Windows::Forms::DialogResult result = HUDForm::ShowModal(CppStringToNative(title), CppStringToNative(text), true, deleg);
 
694
  
 
695
  // Abort is used if the window was forcibly closed (e.g. when showing another dialog, like password query).
 
696
  return (result == Windows::Forms::DialogResult::OK) || (result == Windows::Forms::DialogResult::Abort);
 
697
}
 
698
 
 
699
//--------------------------------------------------------------------------------------------------
 
700
 
 
701
/**
 
702
 * Signals the operation being described in a previous run_cancelable_wait_message() call has
 
703
 * finished and the message panel should be taken down.
 
704
 */
 
705
void UtilitiesImpl::stop_cancelable_wait_message()
 
706
{
 
707
  HUDForm::Finished();
 
708
}
 
709
 
 
710
//--------------------------------------------------------------------------------------------------
 
711
 
 
712
/**
 
713
 * Places the given string on the clipboard.
 
714
 * 
 
715
 * @param content The text to be placed on the clipboard. It is assume its encoding is UTF-8.
 
716
 */
 
717
void UtilitiesImpl::set_clipboard_text(const string &content)
 
718
{
 
719
  if (!content.empty())
 
720
    Clipboard::SetText(CppStringToNative(content));
 
721
}
 
722
 
 
723
//--------------------------------------------------------------------------------------------------
 
724
 
 
725
/**
 
726
 * Returns the current text on the clipboard, if there is any.
 
727
 * 
 
728
 * @result If there is text on the clipboard (ANSI or Unicode) it is returned as UTF-8 string.
 
729
 * @note The returned text gets all CRLF Windows line breaks converted to pure LF.
 
730
 */
 
731
string UtilitiesImpl::get_clipboard_text()
 
732
{
 
733
  String^ unicode= (String^) Clipboard::GetData(DataFormats::UnicodeText);
 
734
  if (unicode == nullptr)
 
735
    return "";
 
736
 
 
737
  return NativeToCppString(unicode);
 
738
}
 
739
 
 
740
//--------------------------------------------------------------------------------------------------
 
741
 
 
742
/**
 
743
 * Returns platform specific user folders, e.g. for the desktop, the user's documents etc.
 
744
 */
 
745
string UtilitiesImpl::get_special_folder(FolderType type)
 
746
{
 
747
  Environment::SpecialFolder special_folder;
 
748
  switch (type)
 
749
  {
 
750
  case Desktop:
 
751
    special_folder = Environment::SpecialFolder::DesktopDirectory;
 
752
    break;
 
753
  case ApplicationData:
 
754
    special_folder = Environment::SpecialFolder::ApplicationData;
 
755
    break;
 
756
  case WinProgramFiles:
 
757
    special_folder = Environment::SpecialFolder::ProgramFiles;
 
758
    break;
 
759
  case WinProgramFilesX86:
 
760
    special_folder = Environment::SpecialFolder::ProgramFilesX86;
 
761
    break;
 
762
  default: // Documents
 
763
    special_folder = Environment::SpecialFolder::MyDocuments;
 
764
    break;
 
765
  };
 
766
 
 
767
  // Getting the 64 bit application folder works differently if we are a 32 bit application.
 
768
  // IntPtr has a size of 8 for 64 bit apps.
 
769
  if (IntPtr::Size == 4 && type == mforms::WinProgramFiles)
 
770
  {
 
771
    WCHAR folder[MAX_PATH];
 
772
    ExpandEnvironmentStrings(L"%ProgramW6432%", folder, ARRAYSIZE(folder));
 
773
    return base::wstring_to_string(folder);
 
774
  }
 
775
  return NativeToCppString(Environment::GetFolderPath(special_folder));
 
776
}
 
777
 
 
778
//--------------------------------------------------------------------------------------------------
 
779
 
 
780
void UtilitiesImpl::open_url(const string &url)
 
781
{
 
782
  try
 
783
  {
 
784
    System::Diagnostics::Process::Start(CppStringToNative(url));
 
785
  }
 
786
  catch (Exception ^e)
 
787
  {
 
788
    MessageBox::Show(e->Message->ToString(), "Error Opening Browser", 
 
789
      MessageBoxButtons::OK, MessageBoxIcon::Error, MessageBoxDefaultButton::Button1);
 
790
  }
 
791
}
 
792
 
 
793
//--------------------------------------------------------------------------------------------------
 
794
 
 
795
bool UtilitiesImpl::move_to_trash(const string &file_name)
 
796
{
 
797
  SHFILEOPSTRUCT shf = {0};
 
798
 
 
799
  // Filenames must be double 0 terminated.
 
800
  wchar_t path[MAX_PATH + 1] = {0};
 
801
 
 
802
  shf.wFunc = FO_DELETE;
 
803
  shf.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION;
 
804
 
 
805
  // Paths must be double 0 terminated.
 
806
  std::wstring converted_filename = base::string_to_wstring(file_name);
 
807
  StringCchCopyW(path, sizeof(path), converted_filename.c_str()); 
 
808
  shf.pFrom = path;
 
809
  int result = SHFileOperation(&shf); 
 
810
 
 
811
  return (result == 0) && !shf.fAnyOperationsAborted;
 
812
}
 
813
 
 
814
//--------------------------------------------------------------------------------------------------
 
815
 
 
816
// The password cache is a temporary storage and only used for a short time frame when looking up a password.
 
817
static GStaticMutex password_mutex= G_STATIC_MUTEX_INIT;
 
818
static std::map<std::string, std::string> password_cache;
 
819
typedef std::map<std::string, std::string>::iterator PasswordIterator;
 
820
 
 
821
#define DOMAIN_SEPARATOR (char) 2
 
822
#define PASSWORD_SEPARATOR (char) 3
 
823
 
 
824
/**
 
825
 * Loads encrypted passwords from disk. These are typically held only for a short moment.
 
826
 */
 
827
void UtilitiesImpl::load_passwords()
 
828
{
 
829
  // Load password cache from disk. Don't throw an error if the cache file doesn't exist yet, though.
 
830
  std::string file= get_special_folder(ApplicationData) + "/MySQL/Workbench/workbench_user_data.dat";
 
831
  if (g_file_test(file.c_str(), G_FILE_TEST_EXISTS))
 
832
  {
 
833
    gchar* content;
 
834
    gsize length;
 
835
    GError* error= NULL;
 
836
    bool result= g_file_get_contents(file.c_str(), &content, &length, &error) == TRUE;
 
837
    if (!result)
 
838
    {
 
839
      show_error("Password management error", "Error while loading passwords: " + std::string(error->message),
 
840
        _("Close"), "", "");
 
841
      return;
 
842
    }
 
843
 
 
844
    DATA_BLOB data_in;
 
845
    DATA_BLOB data_out;
 
846
 
 
847
    data_in.pbData= (BYTE*) content;
 
848
    data_in.cbData= length;
 
849
    result= CryptUnprotectData(&data_in, NULL, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &data_out) == TRUE;
 
850
    
 
851
    g_free(content);
 
852
 
 
853
    if (!result)
 
854
    {
 
855
      show_error("Password management error", "Could not decrypt password cache.",  _("Close"), "", "");
 
856
      return;
 
857
    }
 
858
 
 
859
    try
 
860
    {
 
861
      // Split the string into individual items and fill the password cache with them.
 
862
      std::stringstream ss((char*) data_out.pbData);
 
863
      std::string item;
 
864
      while (std::getline(ss, item, '\n'))
 
865
      {
 
866
        string::size_type pos= item.find_first_of(PASSWORD_SEPARATOR, 0);
 
867
 
 
868
        if (string::npos != pos)
 
869
          password_cache[item.substr(0, pos)]= item.substr(pos + 1, item.length());
 
870
      }
 
871
    }
 
872
    finally
 
873
    {
 
874
      LocalFree(data_out.pbData);
 
875
    }
 
876
  }
 
877
}
 
878
 
 
879
//--------------------------------------------------------------------------------------------------
 
880
 
 
881
/**
 
882
 * Saves the password cache to disk (if store is true) and clears it so passwords aren't kept in
 
883
 * memory any longer than necessary.
 
884
 */
 
885
void UtilitiesImpl::unload_passwords(bool store)
 
886
{
 
887
  // Store all passwords in a string for encryption.
 
888
  try
 
889
  {
 
890
    if (store)
 
891
    {
 
892
      std::string plain_data;
 
893
      for (PasswordIterator iterator= password_cache.begin(); iterator != password_cache.end(); iterator++)
 
894
        plain_data += iterator->first + PASSWORD_SEPARATOR + iterator->second + "\n";
 
895
 
 
896
      DATA_BLOB data_in;
 
897
      DATA_BLOB data_out;
 
898
 
 
899
      data_in.pbData= (BYTE*) plain_data.c_str();
 
900
      data_in.cbData= (DWORD) plain_data.length() + 1;
 
901
      
 
902
      if (!CryptProtectData(&data_in, NULL, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &data_out))
 
903
      {
 
904
        show_error("Password management error", "Could not encrypt password cache.", _("Close"), "", "");
 
905
        return;
 
906
      }
 
907
 
 
908
      // Now write the encrypted data to file.
 
909
      std::string file= get_special_folder(ApplicationData) + "/MySQL/Workbench/workbench_user_data.dat";
 
910
      GError* error= NULL;
 
911
      bool result= g_file_set_contents(file.c_str(), (gchar*) data_out.pbData, data_out.cbData, &error) == TRUE;
 
912
 
 
913
      LocalFree(data_out.pbData);
 
914
 
 
915
      if (!result)
 
916
        show_error("Password management error", "Error while storing passwords: " + std::string(error->message),
 
917
        _("Close"), "", "");
 
918
    }
 
919
  }
 
920
  finally
 
921
  {
 
922
    password_cache.clear();
 
923
  }
 
924
}
 
925
 
 
926
//--------------------------------------------------------------------------------------------------
 
927
 
 
928
/**
 
929
 * This function stores the given password in our password file, parameterized by the given service
 
930
 * and user name. The file is encrypted by the system for safety and can only be decrypted by the
 
931
 * same user who encrypted it.
 
932
 */
 
933
void UtilitiesImpl::store_password(const std::string &service, const std::string &account, const std::string &password)
 
934
{
 
935
  g_static_mutex_lock(&password_mutex);
 
936
 
 
937
  try
 
938
  {
 
939
    load_passwords();
 
940
 
 
941
    // Keep the password in our password cache and write the entire cache to file after that.
 
942
    password_cache[service + DOMAIN_SEPARATOR + account]= password;
 
943
 
 
944
    unload_passwords(true);
 
945
  }
 
946
  finally
 
947
  {
 
948
    g_static_mutex_unlock(&password_mutex);
 
949
  }
 
950
}
 
951
 
 
952
//--------------------------------------------------------------------------------------------------
 
953
 
 
954
/**
 
955
 * Return the plain text password for the given service and account.
 
956
 */
 
957
bool UtilitiesImpl::find_password(const std::string &service, const std::string &account, std::string &password)
 
958
{
 
959
  g_static_mutex_lock(&password_mutex);
 
960
 
 
961
  try
 
962
  {
 
963
    load_passwords();
 
964
 
 
965
    bool result= true;
 
966
    PasswordIterator iterator= password_cache.find(service + DOMAIN_SEPARATOR + account);
 
967
    if (iterator == password_cache.end())
 
968
      result= false;
 
969
    else
 
970
      password= iterator->second;
 
971
 
 
972
    unload_passwords(false);
 
973
    return result;
 
974
  }
 
975
  finally
 
976
  {
 
977
    g_static_mutex_unlock(&password_mutex);
 
978
  }
 
979
}
 
980
 
 
981
//--------------------------------------------------------------------------------------------------
 
982
 
 
983
/**
 
984
 * Remove the password for the given service and account if there is one.
 
985
 */
 
986
void UtilitiesImpl::forget_password(const std::string &service, const std::string &account)
 
987
{
 
988
  g_static_mutex_lock(&password_mutex);
 
989
 
 
990
  try
 
991
  {
 
992
    load_passwords();
 
993
 
 
994
    PasswordIterator iterator= password_cache.find(service + DOMAIN_SEPARATOR + account);
 
995
    if (iterator != password_cache.end())
 
996
    {
 
997
      password_cache.erase(iterator);
 
998
      unload_passwords(true);
 
999
    }
 
1000
    else
 
1001
      unload_passwords(false);
 
1002
  }
 
1003
  finally
 
1004
  {
 
1005
    g_static_mutex_unlock(&password_mutex);
 
1006
  }
 
1007
}
 
1008
 
 
1009
//--------------------------------------------------------------------------------------------------
 
1010
 
 
1011
void* UtilitiesImpl::perform_from_main_thread(const boost::function<void* ()>& slot, bool wait)
 
1012
{
 
1013
  return _dispatcher->RunOnMainThread(slot, wait);
 
1014
}
 
1015
 
 
1016
//--------------------------------------------------------------------------------------------------
 
1017
 
 
1018
/**
 
1019
 * Returns the main form of the application.
 
1020
 */
 
1021
Windows::Forms::Form^ UtilitiesImpl::get_mainform()
 
1022
{
 
1023
  return Application::OpenForms["MainForm"];
 
1024
}
 
1025
 
 
1026
//--------------------------------------------------------------------------------------------------
 
1027
 
 
1028
public ref class TimerHandler
 
1029
{
 
1030
public:
 
1031
  TimerHandler(float interval, const boost::function<bool ()> &slot)
 
1032
  {
 
1033
    _timer = gcnew System::Windows::Forms::Timer();
 
1034
 
 
1035
    _slot = new boost::function<bool ()>(slot);
 
1036
 
 
1037
    _timer->Interval = (int) (interval * 1000);
 
1038
    _timer->Tick += gcnew EventHandler(this, &TimerHandler::timer_tick);
 
1039
    _timer->Start();
 
1040
  }
 
1041
 
 
1042
  ~TimerHandler()
 
1043
  {
 
1044
    _timer->Stop();
 
1045
    delete _timer;
 
1046
    delete _slot;
 
1047
  }
 
1048
 
 
1049
private:
 
1050
  boost::function<bool ()> *_slot;
 
1051
  System::Windows::Forms::Timer ^_timer;
 
1052
 
 
1053
  void timer_tick(Object^ sender, System::EventArgs ^e)
 
1054
  {
 
1055
    // emulate behaviour in MacOS X (timers arent fired when in modal loops)
 
1056
    // also works around a deadlock of python when a timer is fired while inside a modal loop
 
1057
    if (!mforms::Utilities::in_modal_loop())
 
1058
    {
 
1059
      _timer->Stop();
 
1060
 
 
1061
      // if callback returns true, then restart the timer
 
1062
      if ((*_slot)())
 
1063
        _timer->Enabled = true;
 
1064
      else
 
1065
        delete this;
 
1066
    }
 
1067
  }
 
1068
};
 
1069
 
 
1070
//-------------------------------------------------------------------------------------------------
 
1071
 
 
1072
void UtilitiesImpl::add_timeout(float interval, const boost::function<bool ()> &slot)
 
1073
{
 
1074
  TimerHandler ^handler = gcnew TimerHandler(interval, slot);
 
1075
}
 
1076
 
 
1077
//-------------------------------------------------------------------------------------------------