~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to src/addins/MacPlatform/MacMenu/MDMenuItem.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// MDMenuItem.cs
 
3
//
 
4
// Author:
 
5
//       Michael Hutchinson <m.j.hutchinson@gmail.com>
 
6
//
 
7
// Copyright (c) 2013 Xamarin Inc.
 
8
//
 
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
 
10
// of this software and associated documentation files (the "Software"), to deal
 
11
// in the Software without restriction, including without limitation the rights
 
12
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
13
// copies of the Software, and to permit persons to whom the Software is
 
14
// furnished to do so, subject to the following conditions:
 
15
//
 
16
// The above copyright notice and this permission notice shall be included in
 
17
// all copies or substantial portions of the Software.
 
18
//
 
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
24
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
25
// THE SOFTWARE.
 
26
 
 
27
using System;
 
28
using MonoMac.AppKit;
 
29
using MonoDevelop.Components.Commands;
 
30
using MonoDevelop.Core;
 
31
using System.Text;
 
32
using MonoMac.Foundation;
 
33
using MonoMac.ObjCRuntime;
 
34
using System.Collections.Generic;
 
35
 
 
36
namespace MonoDevelop.MacIntegration.MacMenu
 
37
{
 
38
        //TODO: autohide, arrray
 
39
        class MDMenuItem : NSMenuItem, IUpdatableMenuItem
 
40
        {
 
41
                public static Selector ActionSel = new Selector ("run:");
 
42
 
 
43
                CommandEntry ce;
 
44
                CommandManager manager;
 
45
 
 
46
                bool isArrayItem;
 
47
 
 
48
                public MDMenuItem (CommandManager manager, CommandEntry ce, ActionCommand command)
 
49
                {
 
50
                        this.ce = ce;
 
51
                        this.manager = manager;
 
52
 
 
53
                        isArrayItem = command.CommandArray;
 
54
 
 
55
                        Target = this;
 
56
                        Action = ActionSel;
 
57
                }
 
58
 
 
59
                public CommandEntry CommandEntry { get { return ce; } }
 
60
 
 
61
                [Export ("run:")]
 
62
                public void Run (NSMenuItem sender)
 
63
                {
 
64
                        var a = sender as MDExpandedArrayItem;
 
65
                        //if the command opens a modal subloop, give cocoa a chance to unhighlight the menu item
 
66
                        GLib.Timeout.Add (1, () => {
 
67
                                if (a != null) {
 
68
                                        manager.DispatchCommand (ce.CommandId, a.Info.DataItem, CommandSource.MainMenu);
 
69
                                } else {
 
70
                                        manager.DispatchCommand (ce.CommandId, CommandSource.MainMenu);
 
71
                                }
 
72
                                return false;
 
73
                        });
 
74
                }
 
75
 
 
76
                //NOTE: This is used to disable the whole menu when there's a modal dialog.
 
77
                // We can justify this because safari 3.2.1 does it ("do you want to close all tabs?").
 
78
                public static bool IsGloballyDisabled {
 
79
                        get {
 
80
                                return !MonoDevelop.Ide.IdeApp.Workbench.HasToplevelFocus;
 
81
                        }
 
82
                }
 
83
 
 
84
                public void Update (MDMenu parent, ref NSMenuItem lastSeparator, ref int index)
 
85
                {
 
86
                        var info = manager.GetCommandInfo (ce.CommandId);
 
87
 
 
88
                        if (!isArrayItem) {
 
89
                                SetItemValues (this, info);
 
90
                                if (!Hidden)
 
91
                                        MDMenu.ShowLastSeparator (ref lastSeparator);
 
92
                                return;
 
93
                        }
 
94
 
 
95
                        Hidden = true;
 
96
 
 
97
                        if (index < parent.Count - 1) {
 
98
                                for (int i = index + 1; i < parent.Count; i++) {
 
99
                                        var nextItem = parent.ItemAt (i);
 
100
                                        if (nextItem == null || nextItem.Target != this)
 
101
                                                break;
 
102
                                        parent.RemoveItemAt (i);
 
103
                                        i--;
 
104
                                }
 
105
                        }
 
106
 
 
107
                        PopulateArrayItems (info.ArrayInfo, parent, ref lastSeparator, ref index);
 
108
                }
 
109
 
 
110
                void PopulateArrayItems (CommandArrayInfo infos, NSMenu parent, ref NSMenuItem lastSeparator, ref int index)
 
111
                {
 
112
                        foreach (CommandInfo ci in infos) {
 
113
                                if (ci.IsArraySeparator) {
 
114
                                        var n = NSMenuItem.SeparatorItem;
 
115
                                        n.Hidden = true;
 
116
                                        n.Target = this;
 
117
                                        lastSeparator = n;
 
118
                                        parent.InsertItem (n, index++);
 
119
                                        continue;
 
120
                                }
 
121
 
 
122
                                var item = new MDExpandedArrayItem {
 
123
                                        Info = ci,
 
124
                                        Target = this,
 
125
                                        Action = ActionSel,
 
126
                                };
 
127
 
 
128
                                if (ci is CommandInfoSet) {
 
129
                                        item.Submenu = new NSMenu ();
 
130
                                        int i = 0;
 
131
                                        NSMenuItem sep = null;
 
132
                                        PopulateArrayItems (((CommandInfoSet)ci).CommandInfos, item.Submenu, ref sep, ref i);
 
133
                                }
 
134
                                SetItemValues (item, ci);
 
135
 
 
136
                                if (!item.Hidden)
 
137
                                        MDMenu.ShowLastSeparator (ref lastSeparator);
 
138
                                parent.InsertItem (item, index++);
 
139
                        }
 
140
                }
 
141
 
 
142
                class MDExpandedArrayItem : NSMenuItem
 
143
                {
 
144
                        public CommandInfo Info;
 
145
                }
 
146
 
 
147
                static void SetItemValues (NSMenuItem item, CommandInfo info)
 
148
                {
 
149
                        item.SetTitleWithMnemonic (GetCleanCommandText (info));
 
150
                        item.Enabled = !IsGloballyDisabled && info.Enabled;
 
151
                        item.Hidden = !info.Visible;
 
152
                        SetAccel (item, info.AccelKey);
 
153
 
 
154
                        if (info.Checked) {
 
155
                                item.State = NSCellStateValue.On;
 
156
                        } else if (info.CheckedInconsistent) {
 
157
                                item.State = NSCellStateValue.Mixed;
 
158
                        } else {
 
159
                                item.State = NSCellStateValue.Off;
 
160
                        }
 
161
                }
 
162
 
 
163
                static void SetAccel (NSMenuItem item, string accelKey)
 
164
                {
 
165
                        uint modeKey;
 
166
                        Gdk.ModifierType modeMod;
 
167
                        uint key;
 
168
                        Gdk.ModifierType mod;
 
169
 
 
170
                        if (!KeyBindingManager.BindingToKeys (accelKey, out modeKey, out modeMod, out key, out mod)) {
 
171
                                item.KeyEquivalent = "";
 
172
                                item.KeyEquivalentModifierMask = (NSEventModifierMask) 0;
 
173
                                return;
 
174
                        }
 
175
 
 
176
                        if (modeKey != 0) {
 
177
                                LoggingService.LogWarning ("Mac menu cannot display accelerators with mode keys ({0})", accelKey);
 
178
                                item.KeyEquivalent = "";
 
179
                                item.KeyEquivalentModifierMask = (NSEventModifierMask) 0;
 
180
                                return;
 
181
                        }
 
182
 
 
183
                        var keyEq = GetKeyEquivalent ((Gdk.Key) key);
 
184
                        item.KeyEquivalent = keyEq;
 
185
                        if (keyEq.Length == 0) {
 
186
                                item.KeyEquivalentModifierMask = 0;
 
187
                        }
 
188
 
 
189
                        NSEventModifierMask outMod = 0;
 
190
                        if ((mod & Gdk.ModifierType.Mod1Mask) != 0) {
 
191
                                outMod |= NSEventModifierMask.AlternateKeyMask;
 
192
                                mod ^= Gdk.ModifierType.Mod1Mask;
 
193
                        }
 
194
                        if ((mod & Gdk.ModifierType.ShiftMask) != 0) {
 
195
                                outMod |= NSEventModifierMask.ShiftKeyMask;
 
196
                                mod ^= Gdk.ModifierType.ShiftMask;
 
197
                        }
 
198
                        if ((mod & Gdk.ModifierType.ControlMask) != 0) {
 
199
                                outMod |= NSEventModifierMask.ControlKeyMask;
 
200
                                mod ^= Gdk.ModifierType.ControlMask;
 
201
                        }
 
202
                        if ((mod & Gdk.ModifierType.MetaMask) != 0) {
 
203
                                outMod |= NSEventModifierMask.CommandKeyMask;
 
204
                                mod ^= Gdk.ModifierType.MetaMask;
 
205
                        }
 
206
 
 
207
                        if (mod != 0) {
 
208
                                LoggingService.LogWarning ("Mac menu cannot display accelerators with modifiers {0}", mod);
 
209
                        }
 
210
                        item.KeyEquivalentModifierMask = outMod;
 
211
                }
 
212
 
 
213
                static string GetKeyEquivalent (Gdk.Key key)
 
214
                {
 
215
                        char c = (char) Gdk.Keyval.ToUnicode ((uint) key);
 
216
                        if (c != 0)
 
217
                                return c.ToString ();
 
218
 
 
219
                        var fk = GetFunctionKey (key);
 
220
                        if (fk != 0)
 
221
                                return ((char) fk).ToString ();
 
222
 
 
223
                        LoggingService.LogError ("Mac menu cannot display key '{0}", key);
 
224
                        return "";
 
225
                }
 
226
 
 
227
                static string GetCleanCommandText (CommandInfo ci)
 
228
                {
 
229
                        string txt = ci.Text;
 
230
                        if (txt == null)
 
231
                                return "";
 
232
 
 
233
                        //FIXME: markup stripping could be done better
 
234
                        var sb = new StringBuilder ();
 
235
                        for (int i = 0; i < txt.Length; i++) {
 
236
                                char ch = txt[i];
 
237
                                if (ch == '_') {
 
238
                                        if (i + 1 < txt.Length && txt[i + 1] == '_') {
 
239
                                                sb.Append ('_');
 
240
                                                i++;
 
241
                                        } else {
 
242
                                                sb.Append ('&');
 
243
                                        }
 
244
                                } else if (!ci.UseMarkup) {
 
245
                                        sb.Append (ch);
 
246
                                } else if (ch == '<') {
 
247
                                        while (++i < txt.Length && txt[i] != '>');
 
248
                                } else if (ch == '&') {
 
249
                                        int j = i;
 
250
                                        while (++i < txt.Length && txt[i] != ';');
 
251
                                        int len = i - j - 1;
 
252
                                        if (len > 0) {
 
253
                                                string entityName = txt.Substring (j + 1, i - j - 1);
 
254
                                                switch (entityName) {
 
255
                                                        case "quot":
 
256
                                                        sb.Append ('"');
 
257
                                                        break;
 
258
                                                        case "amp":
 
259
                                                        sb.Append ('&');
 
260
                                                        break;
 
261
                                                        case "apos":
 
262
                                                        sb.Append ('\'');
 
263
                                                        break;
 
264
                                                        case "lt":
 
265
                                                        sb.Append ('<');
 
266
                                                        break;
 
267
                                                        case "gt":
 
268
                                                        sb.Append ('>');
 
269
                                                        break;
 
270
                                                        default:
 
271
                                                        LoggingService.LogWarning ("Could not de-markup entity '{0}'", entityName);
 
272
                                                        break;
 
273
                                                }
 
274
                                        }
 
275
                                } else {
 
276
                                        sb.Append (ch);
 
277
                                }
 
278
                        }
 
279
 
 
280
                        return sb.ToString ();
 
281
                }
 
282
 
 
283
                static FunctionKey GetFunctionKey (Gdk.Key key)
 
284
                {
 
285
                        switch (key) {
 
286
                        case Gdk.Key.Return:
 
287
                                return (FunctionKey) (uint) '\n';
 
288
                        case Gdk.Key.BackSpace:
 
289
                                return (FunctionKey) 0x08;
 
290
                                // NSBackspaceCharacter
 
291
                        case Gdk.Key.KP_Delete:
 
292
                        case Gdk.Key.Delete:
 
293
                                return (FunctionKey) 0x7F;
 
294
                                // NSDeleteCharacter
 
295
                        case Gdk.Key.KP_Up:
 
296
                        case Gdk.Key.Up:
 
297
                                return FunctionKey.UpArrow;
 
298
                        case Gdk.Key.KP_Down:
 
299
                        case Gdk.Key.Down:
 
300
                                return FunctionKey.DownArrow;
 
301
                        case Gdk.Key.KP_Left:
 
302
                        case Gdk.Key.Left:
 
303
                                return FunctionKey.LeftArrow;
 
304
                        case Gdk.Key.KP_Right:
 
305
                        case Gdk.Key.Right:
 
306
                                return FunctionKey.RightArrow;
 
307
                        case Gdk.Key.F1:
 
308
                                return FunctionKey.F1;
 
309
                        case Gdk.Key.F2:
 
310
                                return FunctionKey.F2;
 
311
                        case Gdk.Key.F3:
 
312
                                return FunctionKey.F3;
 
313
                        case Gdk.Key.F4:
 
314
                                return FunctionKey.F4;
 
315
                        case Gdk.Key.F5:
 
316
                                return FunctionKey.F5;
 
317
                        case Gdk.Key.F6:
 
318
                                return FunctionKey.F6;
 
319
                        case Gdk.Key.F7:
 
320
                                return FunctionKey.F7;
 
321
                        case Gdk.Key.F8:
 
322
                                return FunctionKey.F8;
 
323
                        case Gdk.Key.F9:
 
324
                                return FunctionKey.F9;
 
325
                        case Gdk.Key.F10:
 
326
                                return FunctionKey.F10;
 
327
                        case Gdk.Key.F11:
 
328
                                return FunctionKey.F11;
 
329
                        case Gdk.Key.F12:
 
330
                                return FunctionKey.F12;
 
331
                        case Gdk.Key.F13:
 
332
                                return FunctionKey.F13;
 
333
                        case Gdk.Key.F14:
 
334
                                return FunctionKey.F14;
 
335
                        case Gdk.Key.F15:
 
336
                                return FunctionKey.F15;
 
337
                        case Gdk.Key.F16:
 
338
                                return FunctionKey.F16;
 
339
                        case Gdk.Key.F17:
 
340
                                return FunctionKey.F17;
 
341
                        case Gdk.Key.F18:
 
342
                                return FunctionKey.F18;
 
343
                        case Gdk.Key.F19:
 
344
                                return FunctionKey.F19;
 
345
                        case Gdk.Key.F20:
 
346
                                return FunctionKey.F20;
 
347
                        case Gdk.Key.F21:
 
348
                                return FunctionKey.F21;
 
349
                        case Gdk.Key.F22:
 
350
                                return FunctionKey.F22;
 
351
                        case Gdk.Key.F23:
 
352
                                return FunctionKey.F23;
 
353
                        case Gdk.Key.F24:
 
354
                                return FunctionKey.F24;
 
355
                        case Gdk.Key.F25:
 
356
                                return FunctionKey.F25;
 
357
                        case Gdk.Key.F26:
 
358
                                return FunctionKey.F26;
 
359
                        case Gdk.Key.F27:
 
360
                                return FunctionKey.F27;
 
361
                        case Gdk.Key.F28:
 
362
                                return FunctionKey.F28;
 
363
                        case Gdk.Key.F29:
 
364
                                return FunctionKey.F29;
 
365
                        case Gdk.Key.F30:
 
366
                                return FunctionKey.F30;
 
367
                        case Gdk.Key.F31:
 
368
                                return FunctionKey.F31;
 
369
                        case Gdk.Key.F32:
 
370
                                return FunctionKey.F32;
 
371
                        case Gdk.Key.F33:
 
372
                                return FunctionKey.F33;
 
373
                        case Gdk.Key.F34:
 
374
                                return FunctionKey.F34;
 
375
                        case Gdk.Key.F35:
 
376
                                return FunctionKey.F35;
 
377
                        case Gdk.Key.KP_Insert:
 
378
                        case Gdk.Key.Insert:
 
379
                                return FunctionKey.Insert;
 
380
                        case Gdk.Key.KP_Home:
 
381
                        case Gdk.Key.Home:
 
382
                                return FunctionKey.Home;
 
383
                        case Gdk.Key.Begin:
 
384
                                return FunctionKey.Begin;
 
385
                        case Gdk.Key.KP_End:
 
386
                        case Gdk.Key.End:
 
387
                                return FunctionKey.End;
 
388
                        case Gdk.Key.KP_Page_Up:
 
389
                        case Gdk.Key.Page_Up:
 
390
                                return FunctionKey.PageUp;
 
391
                        case Gdk.Key.KP_Page_Down:
 
392
                        case Gdk.Key.Page_Down:
 
393
                                return FunctionKey.PageDown;
 
394
                        case Gdk.Key.Key_3270_PrintScreen:
 
395
                                return FunctionKey.PrintScreen;
 
396
                        case Gdk.Key.Scroll_Lock:
 
397
                                return FunctionKey.ScrollLock;
 
398
                        case Gdk.Key.Pause:
 
399
                                return FunctionKey.Pause;
 
400
                        case Gdk.Key.Sys_Req:
 
401
                                return FunctionKey.SysReq;
 
402
                        case Gdk.Key.Break:
 
403
                                return FunctionKey.Break;
 
404
                        case Gdk.Key.Key_3270_Reset:
 
405
                                return FunctionKey.Reset;
 
406
                        case Gdk.Key.Menu:
 
407
                                return FunctionKey.Menu;
 
408
                        case Gdk.Key.Print:
 
409
                                return FunctionKey.Print;
 
410
                        case Gdk.Key.Help:
 
411
                                return FunctionKey.Help;
 
412
                        case Gdk.Key.Find:
 
413
                                return FunctionKey.Find;
 
414
                        case Gdk.Key.Undo:
 
415
                                return FunctionKey.Undo;
 
416
                        case Gdk.Key.Redo:
 
417
                                return FunctionKey.Redo;
 
418
                        case Gdk.Key.Execute:
 
419
                                return FunctionKey.Execute;
 
420
                                /*
 
421
                                return FunctionKey.Stop;
 
422
                                return FunctionKey.User;
 
423
                                return FunctionKey.System;
 
424
                                return FunctionKey.ClearLine;
 
425
                                return FunctionKey.ClearDisplay;
 
426
                                return FunctionKey.InsertLine;
 
427
                                return FunctionKey.DeleteLine;
 
428
                                return FunctionKey.InsertChar;
 
429
                                return FunctionKey.DeleteChar;
 
430
                                return FunctionKey.Next;
 
431
                                return FunctionKey.Prev;
 
432
                                return FunctionKey.Select;
 
433
                                return FunctionKey.ModeSwitch;
 
434
                                */
 
435
                        }
 
436
 
 
437
                        return 0;
 
438
                }
 
439
 
 
440
                // "Function-Key Unicodes" from NSEvent reference
 
441
                enum FunctionKey : ushort
 
442
                {
 
443
                        UpArrow = 0xF700,
 
444
                        DownArrow = 0xF701,
 
445
                        LeftArrow = 0xF702,
 
446
                        RightArrow = 0xF703,
 
447
                        F1 = 0xF704,
 
448
                        F2 = 0xF705,
 
449
                        F3 = 0xF706,
 
450
                        F4 = 0xF707,
 
451
                        F5 = 0xF708,
 
452
                        F6 = 0xF709,
 
453
                        F7 = 0xF70A,
 
454
                        F8 = 0xF70B,
 
455
                        F9 = 0xF70C,
 
456
                        F10 = 0xF70D,
 
457
                        F11 = 0xF70E,
 
458
                        F12 = 0xF70F,
 
459
                        F13 = 0xF710,
 
460
                        F14 = 0xF711,
 
461
                        F15 = 0xF712,
 
462
                        F16 = 0xF713,
 
463
                        F17 = 0xF714,
 
464
                        F18 = 0xF715,
 
465
                        F19 = 0xF716,
 
466
                        F20 = 0xF717,
 
467
                        F21 = 0xF718,
 
468
                        F22 = 0xF719,
 
469
                        F23 = 0xF71A,
 
470
                        F24 = 0xF71B,
 
471
                        F25 = 0xF71C,
 
472
                        F26 = 0xF71D,
 
473
                        F27 = 0xF71E,
 
474
                        F28 = 0xF71F,
 
475
                        F29 = 0xF720,
 
476
                        F30 = 0xF721,
 
477
                        F31 = 0xF722,
 
478
                        F32 = 0xF723,
 
479
                        F33 = 0xF724,
 
480
                        F34 = 0xF725,
 
481
                        F35 = 0xF726,
 
482
                        Insert = 0xF727,
 
483
                        Delete = 0xF728,
 
484
                        Home = 0xF729,
 
485
                        Begin = 0xF72A,
 
486
                        End = 0xF72B,
 
487
                        PageUp = 0xF72C,
 
488
                        PageDown = 0xF72D,
 
489
                        PrintScreen = 0xF72E,
 
490
                        ScrollLock = 0xF72F,
 
491
                        Pause = 0xF730,
 
492
                        SysReq = 0xF731,
 
493
                        Break = 0xF732,
 
494
                        Reset = 0xF733,
 
495
                        Stop = 0xF734,
 
496
                        Menu = 0xF735,
 
497
                        User = 0xF736,
 
498
                        System = 0xF737,
 
499
                        Print = 0xF738,
 
500
                        ClearLine = 0xF739,
 
501
                        ClearDisplay = 0xF73A,
 
502
                        InsertLine = 0xF73B,
 
503
                        DeleteLine = 0xF73C,
 
504
                        InsertChar = 0xF73D,
 
505
                        DeleteChar = 0xF73E,
 
506
                        Prev = 0xF73F,
 
507
                        Next = 0xF740,
 
508
                        Select = 0xF741,
 
509
                        Execute = 0xF742,
 
510
                        Undo = 0xF743,
 
511
                        Redo = 0xF744,
 
512
                        Find = 0xF745,
 
513
                        Help = 0xF746,
 
514
                        ModeSwitch = 0xF747
 
515
                }
 
516
        }
 
517
}