tidy up and abstract the GtkApplication concept a bit for OS X integration

git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@6496 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2010-01-15 17:50:03 +00:00
parent 79910087b9
commit a3b0f8c576
12 changed files with 325 additions and 153 deletions

View file

@ -42,6 +42,7 @@
#include "utils.h"
#include <gtkmm2ext/gtkapplication.h>
#include <gtkmm2ext/application.h>
#include <ardour/session.h>
#include <ardour/profile.h>
@ -851,10 +852,29 @@ ARDOUR_UI::build_menu_bar ()
void
ARDOUR_UI::use_menubar_as_top_menubar ()
{
#ifdef GTKOSX
gtk_application_set_menu_bar ((GtkMenuShell*) menu_bar->gobj());
// ige_mac_menu_set_quit_menu_item (some_item->gobj());
#endif
Gtk::Widget* widget;
Application* app = Application::instance ();
/* Quit will be taken of separately */
if ((widget = ActionManager::get_widget ("/ui/Main/Session/Quit"))) {
widget->hide ();
}
GtkApplicationMenuGroup* group = app->add_app_menu_group ();
if ((widget = ActionManager::get_widget ("/ui/Main/Help/About"))) {
app->add_app_menu_item (group, dynamic_cast<MenuItem*>(widget));
}
if ((widget = ActionManager::get_widget ("/ui/Main/WindowMenu/ToggleOptionsEditor"))) {
app->add_app_menu_item (group, dynamic_cast<MenuItem*>(widget));
}
app->set_menu_bar (*menu_bar);
app->ShouldQuit.connect (sigc::mem_fun (*this, &UI::quit));
app->ShouldLoad.connect (sigc::mem_fun (*this, &ARDOUR_UI::idle_load));
}
void

View file

@ -26,79 +26,10 @@
#include "actions.h"
#include "opts.h"
sigc::signal<void,bool> ApplicationActivationChanged;
@interface AppNotificationObject : NSObject {}
- (AppNotificationObject*) init;
@end
@implementation AppNotificationObject
- (AppNotificationObject*) init
{
self = [ super init ];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(appDidBecomeActive:)
name:NSApplicationDidBecomeActiveNotification
object:[NSApplication sharedApplication]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(appDidBecomeInactive:)
name:NSApplicationWillResignActiveNotification
object:[NSApplication sharedApplication]];
}
return self;
}
- (void)appDidBecomeActive:(NSNotification *)notification
{
ApplicationActivationChanged (true);
}
- (void)appDidBecomeInactive:(NSNotification *)notification
{
ApplicationActivationChanged (false);
}
@end
@interface ArdourApplicationDelegate : NSObject {}
@end
@implementation ArdourApplicationDelegate
-(BOOL) application:(NSApplication*) theApplication openFile:(NSString*) file
{
Glib::ustring utf8_path ([file UTF8String]);
ARDOUR_UI::instance()->idle_load (utf8_path);
return 1;
}
- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication *)sender
{
Gtkmm2ext::UI::instance()->quit ();
return NSTerminateCancel;
}
@end
void
ARDOUR_UI::platform_specific ()
{
Gtk::Widget* widget;
GtkApplicationMenuGroup* group = gtk_application_add_app_menu_group ();
widget = ActionManager::get_widget ("/ui/Main/Help/About");
if (widget) {
gtk_application_add_app_menu_item (group, (GtkMenuItem*) widget->gobj(), 0);
}
widget = ActionManager::get_widget ("/ui/Main/WindowMenu/ToggleOptionsEditor");
if (widget) {
gtk_application_add_app_menu_item (group, (GtkMenuItem*) widget->gobj(), 0);
}
[ NSApp finishLaunching ];
gtk_application_ready ();
if (!ARDOUR_COMMAND_LINE::finder_invoked_ardour) {
@ -111,11 +42,6 @@ ARDOUR_UI::platform_specific ()
void
ARDOUR_UI::platform_setup ()
{
/* this will stick around for ever ... is that OK ? */
[ [AppNotificationObject alloc] init];
[ NSApp setDelegate: [ArdourApplicationDelegate new]];
}
bool

View file

@ -257,12 +257,14 @@ OptionEditor::add_session_paths ()
session_raid_entry.set_text(session->raid_path());
}
#ifndef GTKOSX
static void
font_scale_changed (Gtk::Adjustment* adj)
{
Config->set_font_scale((long)floor (adj->get_value() * 1024));
reset_dpi();
}
#endif
void
OptionEditor::setup_misc_options ()

View file

@ -33,6 +33,7 @@
#include <gtkmm2ext/utils.h>
#include <gtkmm2ext/doi.h>
#include <gtkmm2ext/slider_controller.h>
#include <gtkmm2ext/application.h>
#include <midi++/manager.h>
@ -262,8 +263,7 @@ PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
add (*box);
non_gtk_gui = true;
extern sigc::signal<void,bool> ApplicationActivationChanged;
ApplicationActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
return true;
#endif

View file

@ -37,6 +37,7 @@ if gtkmm2ext['IS_OSX']:
extra_sources = []
gtkmm2ext_files = Split("""
application.cc
auto_spin.cc
barcontroller.cc
binding_proxy.cc
@ -46,6 +47,7 @@ dndtreeview.cc
fastmeter.cc
focus_entry.cc
grouped_buttons.cc
gtkapplication.c
gtk_ui.cc
idle_adjustment.cc
pixfader.cc
@ -60,7 +62,6 @@ textviewer.cc
utils.cc
version.cc
window_title.cc
gtkapplication.c
""")
x11_files=Split("""

View file

@ -0,0 +1,78 @@
/* GTK+ Integration with platform-specific application-wide features
* such as the OS X menubar and application delegate concepts.
*
* Copyright (C) 2007 Pioneer Research Center USA, Inc.
* Copyright (C) 2007 Imendio AB
* Copyright (C) 2009 Paul Davis
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <gtkmm/menushell.h>
#include <gtkmm/menuitem.h>
#include "gtkmm2ext/application.h"
#include "gtkmm2ext/gtkapplication.h"
using namespace Gtk;
using namespace Gtkmm2ext;
Application* Application::_instance = 0;
Application*
Application::instance ()
{
if (!_instance) {
_instance = new Application;
}
return _instance;
}
Application::Application ()
{
gtk_application_init ();
}
Application::~Application ()
{
_instance = 0;
gtk_application_cleanup ();
}
void
Application::ready ()
{
gtk_application_ready ();
}
void
Application::set_menu_bar (MenuShell& shell)
{
gtk_application_set_menu_bar (shell.gobj());
}
GtkApplicationMenuGroup*
Application::add_app_menu_group ()
{
return gtk_application_add_app_menu_group ();
}
void
Application::add_app_menu_item (GtkApplicationMenuGroup* group,
MenuItem* item)
{
gtk_application_add_app_menu_item (group, item->gobj());
}

View file

@ -33,7 +33,7 @@
#include <pbd/pthread_utils.h>
#include <pbd/stacktrace.h>
#include <gtkmm2ext/gtkapplication.h>
#include <gtkmm2ext/application.h>
#include <gtkmm2ext/gtk_ui.h>
#include <gtkmm2ext/textviewer.h>
@ -62,7 +62,6 @@ BaseUI::RequestType Gtkmm2ext::AddTimeout = BaseUI::new_request_type();
#include <pbd/abstract_ui.cc> /* instantiate the template */
UI::UI (string namestr, int *argc, char ***argv)
: AbstractUI<UIRequest> (namestr, true)
{
@ -105,14 +104,16 @@ UI::UI (string namestr, int *argc, char ***argv)
register_thread (pthread_self(), X_("GUI"));
gtk_application_init ();
/* instantiate the Application singleton */
Application::instance();
}
UI::~UI ()
{
delete Application::instance ();
}
bool
UI::caller_is_ui_thread ()
{

View file

@ -30,6 +30,7 @@
#import <AppKit/NSEvent.h>
#import <AppKit/NSApplication.h>
#import <Foundation/NSString.h>
#import <Foundation/NSNotification.h>
#include <string.h>
#include <gtk/gtk.h>
@ -37,6 +38,9 @@
#include <gtkmm2ext/gtkapplication.h>
#include <gtkmm2ext/gtkapplication-private.h>
#define DEBUG(format, ...) g_printerr ("%s: " format, G_STRFUNC, ## __VA_ARGS__)
#define DEBUG(format, ...)
/* TODO
*
* - Sync adding/removing/reordering items
@ -561,7 +565,6 @@ idle_call_activate (gpointer data)
}
- (void) activate:(id) sender
{
printf ("will call %s\n", [[self title] cStringUsingEncoding:NSUTF8StringEncoding]);
g_idle_add (idle_call_activate, gtk_menu_item);
}
@end
@ -996,13 +999,52 @@ cocoa_menu_item_connect (GtkWidget* menu_item,
G_CALLBACK (cocoa_menu_item_notify_label),
menu_item);
}
gtk_widget_hide (GTK_WIDGET (menu_item));
}
static void
sync_menu_item (NSMenuItem* cocoa_item, GtkWidget* menu_item)
add_menu_item (NSMenu* cocoa_menu, GtkWidget* menu_item, int index)
{
GtkWidget* label = NULL;
GNSMenuItem *cocoa_item;
DEBUG ("add %s to menu %s separator ? %d\n", get_menu_label_text (menu_item, NULL),
[[cocoa_menu title] cStringUsingEncoding:NSUTF8StringEncoding],
GTK_IS_SEPARATOR_MENU_ITEM(menu_item));
cocoa_item = cocoa_menu_item_get (menu_item);
if (cocoa_item)
return;
if (GTK_IS_SEPARATOR_MENU_ITEM (menu_item)) {
cocoa_item = [NSMenuItem separatorItem];
DEBUG ("\ta separator\n");
} else {
if (!GTK_WIDGET_VISIBLE (menu_item)) {
DEBUG ("\tnot visible\n");
return;
}
const gchar* label_text = get_menu_label_text (menu_item, &label);
if (label_text)
cocoa_item = [ [ GNSMenuItem alloc] initWithTitle:[ [ NSString alloc] initWithCString:label_text encoding:NSUTF8StringEncoding]
andGtkWidget:(GtkMenuItem*)menu_item];
else
cocoa_item = [ [ GNSMenuItem alloc] initWithTitle:@"" andGtkWidget:(GtkMenuItem*)menu_item];
DEBUG ("\tan item\n");
}
/* connect GtkMenuItem and NSMenuItem so that we can notice changes to accel/label/submenu etc. */
cocoa_menu_item_connect (menu_item, (GNSMenuItem*) cocoa_item, label);
[ cocoa_item setEnabled:YES];
if (index >= 0)
[ cocoa_menu insertItem:cocoa_item atIndex:index];
else
[ cocoa_menu addItem:cocoa_item];
if (!GTK_WIDGET_IS_SENSITIVE (menu_item))
[cocoa_item setState:NSOffState];
@ -1020,36 +1062,6 @@ sync_menu_item (NSMenuItem* cocoa_item, GtkWidget* menu_item)
if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu_item)))
cocoa_menu_item_update_submenu (cocoa_item, menu_item);
}
static void
add_menu_item (NSMenu* cocoa_menu, GtkWidget* menu_item)
{
GtkWidget* label = NULL;
GNSMenuItem *cocoa_item;
if (!GTK_WIDGET_VISIBLE (menu_item))
return;
if (GTK_IS_SEPARATOR_MENU_ITEM (menu_item))
cocoa_item = [NSMenuItem separatorItem];
else {
const gchar* label_text = get_menu_label_text (menu_item, &label);
if (label_text)
cocoa_item = [ [ GNSMenuItem alloc] initWithTitle:[ [ NSString alloc] initWithCString:label_text encoding:NSUTF8StringEncoding]
andGtkWidget:(GtkMenuItem*)menu_item];
else
cocoa_item = [ [ GNSMenuItem alloc] initWithTitle:@"" andGtkWidget:(GtkMenuItem*)menu_item];
}
/* connect GtkMenuItem and NSMenuItem so that we can notice changes to accel/label/submenu etc. */
cocoa_menu_item_connect (menu_item, (GNSMenuItem*) cocoa_item, label);
[ cocoa_item setEnabled:YES];
[ cocoa_menu addItem:cocoa_item];
sync_menu_item (cocoa_item, menu_item);
[ cocoa_item release];
}
@ -1068,7 +1080,6 @@ push_menu_shell_to_nsmenu (GtkMenuShell *menu_shell,
for (l = children; l; l = l->next)
{
GtkWidget *menu_item = (GtkWidget*) l->data;
NSMenuItem* cocoa_item;
if (GTK_IS_TEAROFF_MENU_ITEM (menu_item))
continue;
@ -1076,12 +1087,7 @@ push_menu_shell_to_nsmenu (GtkMenuShell *menu_shell,
if (g_object_get_data (G_OBJECT (menu_item), "gtk-empty-menu-item"))
continue;
cocoa_item = cocoa_menu_item_get (menu_item);
if (!cocoa_item)
add_menu_item (cocoa_menu, menu_item);
else
sync_menu_item (cocoa_item, menu_item);
add_menu_item (cocoa_menu, menu_item, -1);
}
g_list_free (children);
@ -1118,14 +1124,6 @@ parent_set_emission_hook (GSignalInvocationHint *ihint,
if (cocoa_menu)
{
#if 0
g_printerr ("%s: item %s %p (%s, %s)\n", G_STRFUNC,
previous_parent ? "removed from" : "added to",
menu_shell,
get_menu_label_text (instance, NULL),
g_type_name (G_TYPE_FROM_INSTANCE (instance)));
#endif
push_menu_shell_to_nsmenu (GTK_MENU_SHELL (menu_shell),
cocoa_menu,
cocoa_menu == (NSMenu*) data,
@ -1294,8 +1292,7 @@ gtk_application_set_menu_bar (GtkMenuShell *menu_shell)
extern "C" void
gtk_application_add_app_menu_item (GtkApplicationMenuGroup *group,
GtkMenuItem *menu_item,
const gchar *label)
GtkMenuItem *menu_item)
{
// we know that the application menu is always the submenu of the first item in the main menu
NSMenu* mainMenu;
@ -1334,9 +1331,11 @@ gtk_application_add_app_menu_item (GtkApplicationMenuGroup *group,
[appMenu insertItem:[NSMenuItem separatorItem] atIndex:index+1];
index++;
}
add_menu_item (appMenu, GTK_WIDGET(menu_item));
DEBUG ("Add to APP menu bar %s\n", get_menu_label_text (GTK_WIDGET(menu_item), NULL));
add_menu_item (appMenu, GTK_WIDGET(menu_item), index+1);
group->items = g_list_append (group->items, menu_item);
gtk_widget_hide (GTK_WIDGET (menu_item));
return;
}
}
@ -1346,25 +1345,108 @@ gtk_application_add_app_menu_item (GtkApplicationMenuGroup *group,
G_STRFUNC, group);
}
/* application delegate, currently in C++ */
#include <gtkmm2ext/application.h>
#include <glibmm/ustring.h>
namespace Gtk {
namespace Application {
sigc::signal<void,bool> ActivationChanged;
sigc::signal<void,const Glib::ustring&> ShouldLoad;
sigc::signal<void> ShouldQuit;
}
}
@interface GtkApplicationNotificationObject : NSObject {}
- (GtkApplicationNotificationObject*) init;
@end
@implementation GtkApplicationNotificationObject
- (GtkApplicationNotificationObject*) init
{
self = [ super init ];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(appDidBecomeActive:)
name:NSApplicationDidBecomeActiveNotification
object:[NSApplication sharedApplication]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(appDidBecomeInactive:)
name:NSApplicationWillResignActiveNotification
object:[NSApplication sharedApplication]];
}
return self;
}
- (void)appDidBecomeActive:(NSNotification *)notification
{
Gtkmm2ext::Application::instance()->ActivationChanged (true);
}
- (void)appDidBecomeInactive:(NSNotification *)notification
{
Gtkmm2ext::Application::instance()->ActivationChanged (false);
}
@end
@interface GtkApplicationDelegate : NSObject {}
@end
@implementation GtkApplicationDelegate
-(BOOL) application:(NSApplication*) theApplication openFile:(NSString*) file
{
Glib::ustring utf8_path ([file UTF8String]);
Gtkmm2ext::Application::instance()->ShouldLoad (utf8_path);
return 1;
}
- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication *)sender
{
Gtkmm2ext::Application::instance()->ShouldQuit ();
return NSTerminateCancel;
}
@end
/* Basic setup */
extern "C" int
gtk_application_init ()
{
_main_menubar = [[NSMenu alloc] initWithTitle: @""];
if (_main_menubar) {
[NSApp setMainMenu: _main_menubar];
create_apple_menu ();
create_window_menu ();
return 0;
}
return -1;
if (!_main_menubar)
return -1;
[NSApp setMainMenu: _main_menubar];
create_apple_menu ();
// create_window_menu ();
/* this will stick around for ever ... is that OK ? */
[ [GtkApplicationNotificationObject alloc] init];
[ NSApp setDelegate: [GtkApplicationDelegate new]];
return 0;
}
extern "C" void
gtk_application_ready ()
{
[ NSApp finishLaunching ];
}
extern "C" void
gtk_application_cleanup()
{
[ _window_menu release ];
[ _app_menu release ];
[ _main_menubar release ];
if (_window_menu)
[ _window_menu release ];
if (_app_menu)
[ _app_menu release ];
if (_main_menubar)
[ _main_menubar release ];
}

View file

@ -40,8 +40,7 @@ gtk_application_set_menu_bar (GtkMenuShell *menu_shell)
void
gtk_application_add_app_menu_item (GtkApplicationMenuGroup *group,
GtkMenuItem *menu_item,
const gchar *label)
GtkMenuItem *menu_item)
{
}

View file

@ -0,0 +1,62 @@
/* GTK+ Integration with platform-specific application-wide features
* such as the OS X menubar and application delegate concepts.
*
* Copyright (C) 2009 Paul Davis
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GTK_APPLICATION_MM_H__
#define __GTK_APPLICATION_MM_H__
#include <gtkmm2ext/gtkapplication.h> // for GtkApplicationGroup typedef
#include <sigc++/signal.h>
namespace Gtk {
class MenuItem;
class MenuShell;
}
namespace Glib {
class ustring;
}
namespace Gtkmm2ext {
class Application
{
public:
static Application* instance();
~Application ();
void ready ();
void set_menu_bar (Gtk::MenuShell&);
GtkApplicationMenuGroup* add_app_menu_group ();
void add_app_menu_item (GtkApplicationMenuGroup*, Gtk::MenuItem*);
sigc::signal<void,bool> ActivationChanged;
sigc::signal<void,const Glib::ustring&> ShouldLoad;
sigc::signal<void> ShouldQuit;
private:
Application ();
static Application* _instance;
};
}
#endif /* __GTK_APPLICATION_MM_H__ */

View file

@ -142,6 +142,7 @@ class UI : public Receiver, public AbstractUI<UIRequest>
protected:
bool _auto_display_errors;
virtual void handle_fatal (const char *);
virtual void display_message (const char *prefix, gint prefix_len,
Glib::RefPtr<Gtk::TextBuffer::Tag> ptag,

View file

@ -31,13 +31,13 @@ G_BEGIN_DECLS
typedef struct _GtkApplicationMenuGroup GtkApplicationMenuGroup;
int gtk_application_init ();
void gtk_application_ready ();
void gtk_application_cleanup ();
void gtk_application_set_menu_bar (GtkMenuShell *menu_shell);
GtkApplicationMenuGroup * gtk_application_add_app_menu_group (void);
void gtk_application_add_app_menu_item (GtkApplicationMenuGroup *group,
GtkMenuItem *menu_item,
const gchar *label);
GtkMenuItem *menu_item);
/* these are private but here until GtkApplication becomes a GtkObject with an interface */