mirror of
https://github.com/edmccard/twoapple-reboot.git
synced 2024-09-27 04:54:53 +00:00
453 lines
11 KiB
D
453 lines
11 KiB
D
/+
|
|
+ ui/mainwindow.d
|
|
+
|
|
+ Copyright: 2007 Gerald Stocker
|
|
+
|
|
+ This file is part of twoapple-reboot.
|
|
+
|
|
+ twoapple-reboot is free software; you can redistribute it and/or modify
|
|
+ it under the terms of the GNU General Public License as published by
|
|
+ the Free Software Foundation; either version 2 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ twoapple-reboot 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 General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU General Public License
|
|
+ along with twoapple-reboot; if not, write to the Free Software
|
|
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
+/
|
|
|
|
module ui.mainwindow;
|
|
|
|
import std.string;
|
|
|
|
import gtk.MainWindow;
|
|
import gtkc.gtktypes;
|
|
|
|
import ui.inputevents;
|
|
import ui.sound;
|
|
import ui.monitor;
|
|
import host;
|
|
import system.base;
|
|
|
|
TwoappleMainWindow appWindow;
|
|
|
|
class TwoappleMainWindow : MainWindow
|
|
{
|
|
import gtk.MenuBar;
|
|
import gtk.Widget;
|
|
import gdk.Event;
|
|
import gtk.Toolbar;
|
|
import gtk.ToggleToolButton;
|
|
import gtk.SeparatorToolItem;
|
|
import gtk.ToolButton;
|
|
import gtk.SeparatorToolItem;
|
|
import gtk.MenuItem;
|
|
import gtk.AccelGroup;
|
|
import gtk.VBox;
|
|
import gtk.Toolbar;
|
|
import gtk.Label;
|
|
import gtk.ToolItem;
|
|
import gtk.Alignment;
|
|
import peripheral.base;
|
|
|
|
System system;
|
|
Label speedLabel;
|
|
ToolItem speedItem;
|
|
|
|
// XXX FIXME XXX
|
|
// the system and its ui components need to be separated
|
|
// (so system can switch from ii to iie or whatever,
|
|
// without affecting listeners/pausers/etc. that are connected to
|
|
// the mainwindow)
|
|
this()
|
|
{
|
|
super("Twoapple");
|
|
|
|
addOnKeyPress(&input.onKeyPress);
|
|
addOnKeyRelease(&input.onKeyRelease);
|
|
|
|
auto mainBox = new VBox(false, 3);
|
|
|
|
auto menuBar = new MenuBar();
|
|
auto menu = menuBar.append("_Menu");
|
|
auto item = new MenuItem(&onMenuActivate, "_Read text file",
|
|
"menu.read", true, null, 'r');
|
|
menu.append(item);
|
|
|
|
auto toolBar = new Toolbar();
|
|
toolBar.insert(input.pauseButton, -1);
|
|
auto bootButton = new ToolButton(null, "Boot");
|
|
toolBar.insert(bootButton, -1);
|
|
bootButton.addOnClicked(&onBootClicked);
|
|
toolBar.insert(new SeparatorToolItem());
|
|
auto monoButton = new ToggleToolButton();
|
|
monoButton.setLabel("Mono");
|
|
monoButton.addOnToggled(&onMonoToggle);
|
|
toolBar.insert(monoButton, -1);
|
|
auto toolSpace = new SeparatorToolItem();
|
|
toolSpace.setDraw(false);
|
|
toolSpace.setExpand(true);
|
|
toolBar.insert(toolSpace, -1);
|
|
speedLabel = new Label(" 0%");
|
|
speedItem = new ToolItem();
|
|
speedItem.add(speedLabel);
|
|
toolBar.insert(speedItem, -1);
|
|
toolBar.setStyle(GtkToolbarStyle.TEXT);
|
|
|
|
mainBox.packStart(menuBar, false, false, 0);
|
|
mainBox.packStart(toolBar, false, false, 0);
|
|
mainBox.packStart(monitor, false, false, 0);
|
|
mainBox.packStart(peripheralStatus, false, false, 0);
|
|
|
|
add(mainBox);
|
|
setResizable(0);
|
|
showAll();
|
|
}
|
|
|
|
void onBootClicked(ToolButton b)
|
|
{
|
|
reboot = true;
|
|
}
|
|
|
|
void onMonoToggle(ToggleToolButton b)
|
|
{
|
|
monitor.screen.isColor = (!b.getActive());
|
|
if (input.pauseButton.getActive())
|
|
{
|
|
system.video_.forceFrame();
|
|
}
|
|
}
|
|
|
|
void initSystem(System sys)
|
|
{
|
|
showAll();
|
|
system = sys;
|
|
}
|
|
|
|
void onMenuActivate(MenuItem menuItem)
|
|
{
|
|
TwoappleFile checkFile =
|
|
TwoappleFilePicker.open("text file");
|
|
if (checkFile !is null)
|
|
input.startTextInput(checkFile.fileName);
|
|
}
|
|
|
|
bool windowDelete(Event event, Widget widget)
|
|
{
|
|
stayOpen = false;
|
|
soundCard.pause();
|
|
return super.windowDelete(event, widget);
|
|
}
|
|
|
|
bool configChanged;
|
|
bool runOnce;
|
|
bool stayOpen;
|
|
bool reboot;
|
|
|
|
import std.stream;
|
|
void mainLoop()
|
|
{
|
|
bool shouldRun;
|
|
uint willElapse, didElapse;
|
|
float speedPercent;
|
|
char[] speedString = new char[10];
|
|
stayOpen = true;
|
|
do
|
|
{
|
|
if (configChanged)
|
|
{
|
|
// apply config
|
|
configChanged = false;
|
|
debug(disassemble)
|
|
{
|
|
reboot = false;
|
|
}
|
|
else
|
|
{
|
|
reboot = true;
|
|
}
|
|
}
|
|
|
|
if (reboot)
|
|
{
|
|
system.reboot();
|
|
system.reset();
|
|
input.pauseButton.setSensitive(true);
|
|
input.pauseButton.setActive(false);
|
|
host.delay.reset();
|
|
reboot = false;
|
|
}
|
|
|
|
if (input.pauseButton.getActive())
|
|
shouldRun = runOnce;
|
|
else
|
|
shouldRun = true;
|
|
|
|
if (shouldRun)
|
|
{
|
|
willElapse = system.checkpoint();
|
|
system.execute();
|
|
didElapse = system.sinceCheckpoint(willElapse);
|
|
input.processEvents();
|
|
// XXX do something about typeahead?
|
|
if (!runOnce)
|
|
{
|
|
host.delay.sleep();
|
|
speedPercent = host.delay.checkSpeedPercent(didElapse);
|
|
if (speedPercent > -1.0)
|
|
{
|
|
speedString = std.string.sformat(speedString,
|
|
"% 5d%%", cast(int)(speedPercent * 100));
|
|
speedLabel.setText(speedString.idup);
|
|
}
|
|
}
|
|
runOnce = false;
|
|
}
|
|
else
|
|
{
|
|
input.processEvents();
|
|
// XXX time says "Paused" or something
|
|
if (!(input.pauseButton.getActive()))
|
|
host.delay.reset();
|
|
else
|
|
host.delay.nap();
|
|
}
|
|
}
|
|
while (stayOpen);
|
|
}
|
|
}
|
|
|
|
class TwoappleFile
|
|
{
|
|
import std.c.linux.linux;
|
|
import std.file;
|
|
import std.path;
|
|
|
|
string fileName;
|
|
immutable(char)* fileNameZ;
|
|
|
|
this(string fname)
|
|
{
|
|
assert(isabs(fname) != 0);
|
|
fileName = fname;
|
|
fileNameZ = std.string.toStringz(fname);
|
|
}
|
|
|
|
string folder()
|
|
{
|
|
return getDirName(fileName);
|
|
}
|
|
|
|
string baseName()
|
|
{
|
|
string base = getName(fileName);
|
|
if (base is null) return getBaseName(fileName);
|
|
else return getBaseName(base);
|
|
}
|
|
|
|
bool canRead()
|
|
{
|
|
return !(access(fileNameZ, 4));
|
|
}
|
|
|
|
bool canWrite()
|
|
{
|
|
return !(access(fileNameZ, 2));
|
|
}
|
|
|
|
bool canCreate()
|
|
{
|
|
return !(access(std.string.toStringz(folder()), 2));
|
|
}
|
|
|
|
bool exists()
|
|
{
|
|
return (std.file.exists(fileName) != 0);
|
|
}
|
|
|
|
uint fileSize()
|
|
{
|
|
return cast(uint)getSize(fileName);
|
|
}
|
|
}
|
|
|
|
class TwoappleError
|
|
{
|
|
import gtk.Window;
|
|
import gtk.MessageDialog;
|
|
|
|
static void show(Window parent, string msg)
|
|
{
|
|
scope md = new MessageDialog(parent, GtkDialogFlags.MODAL,
|
|
MessageType.ERROR, ButtonsType.CLOSE, msg);
|
|
md.setTitle("Error");
|
|
md.run();
|
|
md.hide();
|
|
md.destroy();
|
|
}
|
|
|
|
static void show(string msg)
|
|
{
|
|
soundCard.pause();
|
|
show(appWindow, msg);
|
|
host.delay.reset();
|
|
soundCard.resume();
|
|
}
|
|
}
|
|
|
|
class TwoappleFilePicker
|
|
{
|
|
import std.stdio;
|
|
import gtk.FileChooserDialog;
|
|
|
|
static string[string] lastFolder;
|
|
|
|
static TwoappleFile saveAs(string type, string folder, string name)
|
|
{
|
|
soundCard.pause();
|
|
|
|
scope fcd = new FileChooserDialog("Save " ~ type, appWindow,
|
|
FileChooserAction.SAVE);
|
|
|
|
fcd.setCurrentFolder(folder);
|
|
fcd.setCurrentName(name);
|
|
|
|
TwoappleFile file;
|
|
while (true)
|
|
{
|
|
if (fcd.run() != ResponseType.GTK_RESPONSE_OK) break;
|
|
file = new TwoappleFile(fcd.getFilename());
|
|
if (file.exists())
|
|
{
|
|
if (!file.canWrite())
|
|
{
|
|
TwoappleError.show(fcd, "File is read-only");
|
|
file = null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!file.canCreate())
|
|
{
|
|
TwoappleError.show(fcd, "Directory is read-only");
|
|
file = null;
|
|
}
|
|
}
|
|
if (file !is null) break;
|
|
}
|
|
|
|
fcd.hide();
|
|
fcd.destroy();
|
|
|
|
host.delay.reset();
|
|
soundCard.resume();
|
|
|
|
return file;
|
|
}
|
|
|
|
static TwoappleFile open(string type, string delegate(TwoappleFile) dg)
|
|
{
|
|
soundCard.pause();
|
|
|
|
scope fcd = new FileChooserDialog("Open " ~ type, appWindow,
|
|
FileChooserAction.OPEN);
|
|
|
|
if (type in lastFolder)
|
|
fcd.setCurrentFolder(lastFolder[type]);
|
|
|
|
TwoappleFile file;
|
|
while(true)
|
|
{
|
|
if (fcd.run() != ResponseType.GTK_RESPONSE_OK) break;
|
|
file = new TwoappleFile(fcd.getFilename());
|
|
if (!file.canRead())
|
|
{
|
|
TwoappleError.show(fcd, "File cannot be read");
|
|
file = null;
|
|
}
|
|
else
|
|
{
|
|
string msg = dg(file);
|
|
if (msg !is null)
|
|
{
|
|
TwoappleError.show(fcd, msg);
|
|
file = null;
|
|
}
|
|
}
|
|
if (file !is null)
|
|
{
|
|
lastFolder[type] = file.folder();
|
|
break;
|
|
}
|
|
}
|
|
|
|
fcd.hide();
|
|
fcd.destroy();
|
|
|
|
host.delay.reset();
|
|
soundCard.resume();
|
|
|
|
return file;
|
|
}
|
|
|
|
static TwoappleFile open(string type, string function(TwoappleFile) func)
|
|
{
|
|
return open(type,
|
|
delegate string(TwoappleFile checkFile)
|
|
{ return func(checkFile); });
|
|
}
|
|
|
|
static TwoappleFile open(string type)
|
|
{
|
|
return open(type,
|
|
delegate string(TwoappleFile checkFile)
|
|
{ return null; });
|
|
}
|
|
}
|
|
|
|
class TwoappleDialog
|
|
{
|
|
import gtk.MessageDialog;
|
|
|
|
static int run(string title, string msg, string[] buttonText,
|
|
bool hasDefault = true)
|
|
{
|
|
soundCard.pause();
|
|
|
|
scope md = new MessageDialog(appWindow, GtkDialogFlags.MODAL,
|
|
MessageType.WARNING, ButtonsType.NONE, msg);
|
|
md.setTitle(title);
|
|
|
|
int response;
|
|
for (int b = 0; b < buttonText.length; ++b)
|
|
{
|
|
if ((b == 0) && hasDefault)
|
|
response = ResponseType.GTK_RESPONSE_OK;
|
|
else if (b == (buttonText.length - 1))
|
|
response = ResponseType.GTK_RESPONSE_CANCEL;
|
|
else
|
|
response = b;
|
|
md.addButton(buttonText[b], response);
|
|
}
|
|
|
|
response = md.run();
|
|
md.hide();
|
|
md.destroy();
|
|
|
|
if (response == ResponseType.GTK_RESPONSE_OK)
|
|
response = 0;
|
|
else if ((response == ResponseType.GTK_RESPONSE_CANCEL) ||
|
|
(response == ResponseType.GTK_RESPONSE_DELETE_EVENT))
|
|
response = cast(int)(buttonText.length - 1);
|
|
|
|
host.delay.reset();
|
|
soundCard.resume();
|
|
|
|
return response;
|
|
}
|
|
}
|